3 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
5 function getDefaultExportFromCjs (x) {
6 return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
9 function createCommonjsModule(fn, basedir, module) {
13 require: function (path, base) {
14 return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
16 }, fn(module, module.exports), module.exports;
19 function commonjsRequire () {
20 throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
23 var check = function (it) {
24 return it && it.Math == Math && it;
27 // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
29 // eslint-disable-next-line no-undef
30 check(typeof globalThis == 'object' && globalThis) ||
31 check(typeof window == 'object' && window) ||
32 check(typeof self == 'object' && self) ||
33 check(typeof commonjsGlobal == 'object' && commonjsGlobal) ||
34 // eslint-disable-next-line no-new-func
35 Function('return this')();
37 var fails = function (exec) {
45 // Thank's IE8 for his funny defineProperty
46 var descriptors = !fails(function () {
47 return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7;
50 var nativePropertyIsEnumerable = {}.propertyIsEnumerable;
51 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
54 var NASHORN_BUG = getOwnPropertyDescriptor && !nativePropertyIsEnumerable.call({ 1: 2 }, 1);
56 // `Object.prototype.propertyIsEnumerable` method implementation
57 // https://tc39.github.io/ecma262/#sec-object.prototype.propertyisenumerable
58 var f = NASHORN_BUG ? function propertyIsEnumerable(V) {
59 var descriptor = getOwnPropertyDescriptor(this, V);
60 return !!descriptor && descriptor.enumerable;
61 } : nativePropertyIsEnumerable;
63 var objectPropertyIsEnumerable = {
67 var createPropertyDescriptor = function (bitmap, value) {
69 enumerable: !(bitmap & 1),
70 configurable: !(bitmap & 2),
71 writable: !(bitmap & 4),
76 var toString = {}.toString;
78 var classofRaw = function (it) {
79 return toString.call(it).slice(8, -1);
84 // fallback for non-array-like ES3 and non-enumerable old V8 strings
85 var indexedObject = fails(function () {
86 // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
87 // eslint-disable-next-line no-prototype-builtins
88 return !Object('z').propertyIsEnumerable(0);
90 return classofRaw(it) == 'String' ? split.call(it, '') : Object(it);
93 // `RequireObjectCoercible` abstract operation
94 // https://tc39.github.io/ecma262/#sec-requireobjectcoercible
95 var requireObjectCoercible = function (it) {
96 if (it == undefined) throw TypeError("Can't call method on " + it);
100 // toObject with fallback for non-array-like ES3 strings
104 var toIndexedObject = function (it) {
105 return indexedObject(requireObjectCoercible(it));
108 var isObject = function (it) {
109 return typeof it === 'object' ? it !== null : typeof it === 'function';
112 // `ToPrimitive` abstract operation
113 // https://tc39.github.io/ecma262/#sec-toprimitive
114 // instead of the ES6 spec version, we didn't implement @@toPrimitive case
115 // and the second argument - flag - preferred type is a string
116 var toPrimitive = function (input, PREFERRED_STRING) {
117 if (!isObject(input)) return input;
119 if (PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject(val = fn.call(input))) return val;
120 if (typeof (fn = input.valueOf) == 'function' && !isObject(val = fn.call(input))) return val;
121 if (!PREFERRED_STRING && typeof (fn = input.toString) == 'function' && !isObject(val = fn.call(input))) return val;
122 throw TypeError("Can't convert object to primitive value");
125 var hasOwnProperty = {}.hasOwnProperty;
127 var has = function (it, key) {
128 return hasOwnProperty.call(it, key);
131 var document$1 = global_1.document;
132 // typeof document.createElement is 'object' in old IE
133 var EXISTS = isObject(document$1) && isObject(document$1.createElement);
135 var documentCreateElement = function (it) {
136 return EXISTS ? document$1.createElement(it) : {};
139 // Thank's IE8 for his funny defineProperty
140 var ie8DomDefine = !descriptors && !fails(function () {
141 return Object.defineProperty(documentCreateElement('div'), 'a', {
142 get: function () { return 7; }
146 var nativeGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
148 // `Object.getOwnPropertyDescriptor` method
149 // https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptor
150 var f$1 = descriptors ? nativeGetOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) {
151 O = toIndexedObject(O);
152 P = toPrimitive(P, true);
153 if (ie8DomDefine) try {
154 return nativeGetOwnPropertyDescriptor(O, P);
155 } catch (error) { /* empty */ }
156 if (has(O, P)) return createPropertyDescriptor(!objectPropertyIsEnumerable.f.call(O, P), O[P]);
159 var objectGetOwnPropertyDescriptor = {
163 var anObject = function (it) {
165 throw TypeError(String(it) + ' is not an object');
169 var nativeDefineProperty = Object.defineProperty;
171 // `Object.defineProperty` method
172 // https://tc39.github.io/ecma262/#sec-object.defineproperty
173 var f$2 = descriptors ? nativeDefineProperty : function defineProperty(O, P, Attributes) {
175 P = toPrimitive(P, true);
176 anObject(Attributes);
177 if (ie8DomDefine) try {
178 return nativeDefineProperty(O, P, Attributes);
179 } catch (error) { /* empty */ }
180 if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');
181 if ('value' in Attributes) O[P] = Attributes.value;
185 var objectDefineProperty = {
189 var createNonEnumerableProperty = descriptors ? function (object, key, value) {
190 return objectDefineProperty.f(object, key, createPropertyDescriptor(1, value));
191 } : function (object, key, value) {
196 var setGlobal = function (key, value) {
198 createNonEnumerableProperty(global_1, key, value);
200 global_1[key] = value;
204 var SHARED = '__core-js_shared__';
205 var store = global_1[SHARED] || setGlobal(SHARED, {});
207 var sharedStore = store;
209 var functionToString = Function.toString;
211 // this helper broken in `3.4.1-3.4.4`, so we can't use `shared` helper
212 if (typeof sharedStore.inspectSource != 'function') {
213 sharedStore.inspectSource = function (it) {
214 return functionToString.call(it);
218 var inspectSource = sharedStore.inspectSource;
220 var WeakMap = global_1.WeakMap;
222 var nativeWeakMap = typeof WeakMap === 'function' && /native code/.test(inspectSource(WeakMap));
226 var shared = createCommonjsModule(function (module) {
227 (module.exports = function (key, value) {
228 return sharedStore[key] || (sharedStore[key] = value !== undefined ? value : {});
229 })('versions', []).push({
232 copyright: '© 2020 Denis Pushkarev (zloirock.ru)'
237 var postfix = Math.random();
239 var uid = function (key) {
240 return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id + postfix).toString(36);
243 var keys = shared('keys');
245 var sharedKey = function (key) {
246 return keys[key] || (keys[key] = uid(key));
251 var WeakMap$1 = global_1.WeakMap;
254 var enforce = function (it) {
255 return has$1(it) ? get(it) : set(it, {});
258 var getterFor = function (TYPE) {
259 return function (it) {
261 if (!isObject(it) || (state = get(it)).type !== TYPE) {
262 throw TypeError('Incompatible receiver, ' + TYPE + ' required');
268 var store$1 = new WeakMap$1();
269 var wmget = store$1.get;
270 var wmhas = store$1.has;
271 var wmset = store$1.set;
272 set = function (it, metadata) {
273 wmset.call(store$1, it, metadata);
276 get = function (it) {
277 return wmget.call(store$1, it) || {};
279 has$1 = function (it) {
280 return wmhas.call(store$1, it);
283 var STATE = sharedKey('state');
284 hiddenKeys[STATE] = true;
285 set = function (it, metadata) {
286 createNonEnumerableProperty(it, STATE, metadata);
289 get = function (it) {
290 return has(it, STATE) ? it[STATE] : {};
292 has$1 = function (it) {
293 return has(it, STATE);
297 var internalState = {
305 var redefine = createCommonjsModule(function (module) {
306 var getInternalState = internalState.get;
307 var enforceInternalState = internalState.enforce;
308 var TEMPLATE = String(String).split('String');
310 (module.exports = function (O, key, value, options) {
311 var unsafe = options ? !!options.unsafe : false;
312 var simple = options ? !!options.enumerable : false;
313 var noTargetGet = options ? !!options.noTargetGet : false;
314 if (typeof value == 'function') {
315 if (typeof key == 'string' && !has(value, 'name')) createNonEnumerableProperty(value, 'name', key);
316 enforceInternalState(value).source = TEMPLATE.join(typeof key == 'string' ? key : '');
318 if (O === global_1) {
319 if (simple) O[key] = value;
320 else setGlobal(key, value);
322 } else if (!unsafe) {
324 } else if (!noTargetGet && O[key]) {
327 if (simple) O[key] = value;
328 else createNonEnumerableProperty(O, key, value);
329 // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
330 })(Function.prototype, 'toString', function toString() {
331 return typeof this == 'function' && getInternalState(this).source || inspectSource(this);
337 var aFunction = function (variable) {
338 return typeof variable == 'function' ? variable : undefined;
341 var getBuiltIn = function (namespace, method) {
342 return arguments.length < 2 ? aFunction(path[namespace]) || aFunction(global_1[namespace])
343 : path[namespace] && path[namespace][method] || global_1[namespace] && global_1[namespace][method];
346 var ceil = Math.ceil;
347 var floor = Math.floor;
349 // `ToInteger` abstract operation
350 // https://tc39.github.io/ecma262/#sec-tointeger
351 var toInteger = function (argument) {
352 return isNaN(argument = +argument) ? 0 : (argument > 0 ? floor : ceil)(argument);
357 // `ToLength` abstract operation
358 // https://tc39.github.io/ecma262/#sec-tolength
359 var toLength = function (argument) {
360 return argument > 0 ? min(toInteger(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
364 var min$1 = Math.min;
366 // Helper for a popular repeating case of the spec:
367 // Let integer be ? ToInteger(index).
368 // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).
369 var toAbsoluteIndex = function (index, length) {
370 var integer = toInteger(index);
371 return integer < 0 ? max(integer + length, 0) : min$1(integer, length);
374 // `Array.prototype.{ indexOf, includes }` methods implementation
375 var createMethod = function (IS_INCLUDES) {
376 return function ($this, el, fromIndex) {
377 var O = toIndexedObject($this);
378 var length = toLength(O.length);
379 var index = toAbsoluteIndex(fromIndex, length);
381 // Array#includes uses SameValueZero equality algorithm
382 // eslint-disable-next-line no-self-compare
383 if (IS_INCLUDES && el != el) while (length > index) {
385 // eslint-disable-next-line no-self-compare
386 if (value != value) return true;
387 // Array#indexOf ignores holes, Array#includes - not
388 } else for (;length > index; index++) {
389 if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
390 } return !IS_INCLUDES && -1;
394 var arrayIncludes = {
395 // `Array.prototype.includes` method
396 // https://tc39.github.io/ecma262/#sec-array.prototype.includes
397 includes: createMethod(true),
398 // `Array.prototype.indexOf` method
399 // https://tc39.github.io/ecma262/#sec-array.prototype.indexof
400 indexOf: createMethod(false)
403 var indexOf = arrayIncludes.indexOf;
406 var objectKeysInternal = function (object, names) {
407 var O = toIndexedObject(object);
411 for (key in O) !has(hiddenKeys, key) && has(O, key) && result.push(key);
412 // Don't enum bug & hidden keys
413 while (names.length > i) if (has(O, key = names[i++])) {
414 ~indexOf(result, key) || result.push(key);
419 // IE8- don't enum bug keys
424 'propertyIsEnumerable',
430 var hiddenKeys$1 = enumBugKeys.concat('length', 'prototype');
432 // `Object.getOwnPropertyNames` method
433 // https://tc39.github.io/ecma262/#sec-object.getownpropertynames
434 var f$3 = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
435 return objectKeysInternal(O, hiddenKeys$1);
438 var objectGetOwnPropertyNames = {
442 var f$4 = Object.getOwnPropertySymbols;
444 var objectGetOwnPropertySymbols = {
448 // all object keys, includes non-enumerable and symbols
449 var ownKeys = getBuiltIn('Reflect', 'ownKeys') || function ownKeys(it) {
450 var keys = objectGetOwnPropertyNames.f(anObject(it));
451 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
452 return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys;
455 var copyConstructorProperties = function (target, source) {
456 var keys = ownKeys(source);
457 var defineProperty = objectDefineProperty.f;
458 var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
459 for (var i = 0; i < keys.length; i++) {
461 if (!has(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key));
465 var replacement = /#|\.prototype\./;
467 var isForced = function (feature, detection) {
468 var value = data[normalize(feature)];
469 return value == POLYFILL ? true
470 : value == NATIVE ? false
471 : typeof detection == 'function' ? fails(detection)
475 var normalize = isForced.normalize = function (string) {
476 return String(string).replace(replacement, '.').toLowerCase();
479 var data = isForced.data = {};
480 var NATIVE = isForced.NATIVE = 'N';
481 var POLYFILL = isForced.POLYFILL = 'P';
483 var isForced_1 = isForced;
485 var getOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
493 options.target - name of the target object
494 options.global - target is the global object
495 options.stat - export as static methods of target
496 options.proto - export as prototype methods of target
497 options.real - real prototype method for the `pure` version
498 options.forced - export even if the native feature is available
499 options.bind - bind methods to the target, required for the `pure` version
500 options.wrap - wrap constructors to preventing global pollution, required for the `pure` version
501 options.unsafe - use the simple assignment of property instead of delete + defineProperty
502 options.sham - add a flag to not completely full polyfills
503 options.enumerable - export as enumerable property
504 options.noTargetGet - prevent calling a getter on target
506 var _export = function (options, source) {
507 var TARGET = options.target;
508 var GLOBAL = options.global;
509 var STATIC = options.stat;
510 var FORCED, target, key, targetProperty, sourceProperty, descriptor;
514 target = global_1[TARGET] || setGlobal(TARGET, {});
516 target = (global_1[TARGET] || {}).prototype;
518 if (target) for (key in source) {
519 sourceProperty = source[key];
520 if (options.noTargetGet) {
521 descriptor = getOwnPropertyDescriptor$1(target, key);
522 targetProperty = descriptor && descriptor.value;
523 } else targetProperty = target[key];
524 FORCED = isForced_1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);
525 // contained in target
526 if (!FORCED && targetProperty !== undefined) {
527 if (typeof sourceProperty === typeof targetProperty) continue;
528 copyConstructorProperties(sourceProperty, targetProperty);
530 // add a flag to not completely full polyfills
531 if (options.sham || (targetProperty && targetProperty.sham)) {
532 createNonEnumerableProperty(sourceProperty, 'sham', true);
535 redefine(target, key, sourceProperty, options);
540 // https://tc39.github.io/ecma262/#sec-date.now
541 _export({ target: 'Date', stat: true }, {
542 now: function now() {
543 return new Date().getTime();
547 var DatePrototype = Date.prototype;
548 var INVALID_DATE = 'Invalid Date';
549 var TO_STRING = 'toString';
550 var nativeDateToString = DatePrototype[TO_STRING];
551 var getTime = DatePrototype.getTime;
553 // `Date.prototype.toString` method
554 // https://tc39.github.io/ecma262/#sec-date.prototype.tostring
555 if (new Date(NaN) + '' != INVALID_DATE) {
556 redefine(DatePrototype, TO_STRING, function toString() {
557 var value = getTime.call(this);
558 // eslint-disable-next-line no-self-compare
559 return value === value ? nativeDateToString.call(this) : INVALID_DATE;
563 var nativeSymbol = !!Object.getOwnPropertySymbols && !fails(function () {
564 // Chrome 38 Symbol has incorrect toString conversion
565 // eslint-disable-next-line no-undef
566 return !String(Symbol());
569 var useSymbolAsUid = nativeSymbol
570 // eslint-disable-next-line no-undef
572 // eslint-disable-next-line no-undef
573 && typeof Symbol.iterator == 'symbol';
575 // `IsArray` abstract operation
576 // https://tc39.github.io/ecma262/#sec-isarray
577 var isArray = Array.isArray || function isArray(arg) {
578 return classofRaw(arg) == 'Array';
581 // `ToObject` abstract operation
582 // https://tc39.github.io/ecma262/#sec-toobject
583 var toObject = function (argument) {
584 return Object(requireObjectCoercible(argument));
587 // `Object.keys` method
588 // https://tc39.github.io/ecma262/#sec-object.keys
589 var objectKeys = Object.keys || function keys(O) {
590 return objectKeysInternal(O, enumBugKeys);
593 // `Object.defineProperties` method
594 // https://tc39.github.io/ecma262/#sec-object.defineproperties
595 var objectDefineProperties = descriptors ? Object.defineProperties : function defineProperties(O, Properties) {
597 var keys = objectKeys(Properties);
598 var length = keys.length;
601 while (length > index) objectDefineProperty.f(O, key = keys[index++], Properties[key]);
605 var html = getBuiltIn('document', 'documentElement');
609 var PROTOTYPE = 'prototype';
610 var SCRIPT = 'script';
611 var IE_PROTO = sharedKey('IE_PROTO');
613 var EmptyConstructor = function () { /* empty */ };
615 var scriptTag = function (content) {
616 return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT;
619 // Create object with fake `null` prototype: use ActiveX Object with cleared prototype
620 var NullProtoObjectViaActiveX = function (activeXDocument) {
621 activeXDocument.write(scriptTag(''));
622 activeXDocument.close();
623 var temp = activeXDocument.parentWindow.Object;
624 activeXDocument = null; // avoid memory leak
628 // Create object with fake `null` prototype: use iframe Object with cleared prototype
629 var NullProtoObjectViaIFrame = function () {
630 // Thrash, waste and sodomy: IE GC bug
631 var iframe = documentCreateElement('iframe');
632 var JS = 'java' + SCRIPT + ':';
634 iframe.style.display = 'none';
635 html.appendChild(iframe);
636 // https://github.com/zloirock/core-js/issues/475
637 iframe.src = String(JS);
638 iframeDocument = iframe.contentWindow.document;
639 iframeDocument.open();
640 iframeDocument.write(scriptTag('document.F=Object'));
641 iframeDocument.close();
642 return iframeDocument.F;
645 // Check for document.domain and active x support
646 // No need to use active x approach when document.domain is not set
647 // see https://github.com/es-shims/es5-shim/issues/150
648 // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
651 var NullProtoObject = function () {
653 /* global ActiveXObject */
654 activeXDocument = document.domain && new ActiveXObject('htmlfile');
655 } catch (error) { /* ignore */ }
656 NullProtoObject = activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) : NullProtoObjectViaIFrame();
657 var length = enumBugKeys.length;
658 while (length--) delete NullProtoObject[PROTOTYPE][enumBugKeys[length]];
659 return NullProtoObject();
662 hiddenKeys[IE_PROTO] = true;
664 // `Object.create` method
665 // https://tc39.github.io/ecma262/#sec-object.create
666 var objectCreate = Object.create || function create(O, Properties) {
669 EmptyConstructor[PROTOTYPE] = anObject(O);
670 result = new EmptyConstructor();
671 EmptyConstructor[PROTOTYPE] = null;
672 // add "__proto__" for Object.getPrototypeOf polyfill
673 result[IE_PROTO] = O;
674 } else result = NullProtoObject();
675 return Properties === undefined ? result : objectDefineProperties(result, Properties);
678 var nativeGetOwnPropertyNames = objectGetOwnPropertyNames.f;
680 var toString$1 = {}.toString;
682 var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames
683 ? Object.getOwnPropertyNames(window) : [];
685 var getWindowNames = function (it) {
687 return nativeGetOwnPropertyNames(it);
689 return windowNames.slice();
693 // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window
694 var f$5 = function getOwnPropertyNames(it) {
695 return windowNames && toString$1.call(it) == '[object Window]'
697 : nativeGetOwnPropertyNames(toIndexedObject(it));
700 var objectGetOwnPropertyNamesExternal = {
704 var WellKnownSymbolsStore = shared('wks');
705 var Symbol$1 = global_1.Symbol;
706 var createWellKnownSymbol = useSymbolAsUid ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid;
708 var wellKnownSymbol = function (name) {
709 if (!has(WellKnownSymbolsStore, name)) {
710 if (nativeSymbol && has(Symbol$1, name)) WellKnownSymbolsStore[name] = Symbol$1[name];
711 else WellKnownSymbolsStore[name] = createWellKnownSymbol('Symbol.' + name);
712 } return WellKnownSymbolsStore[name];
715 var f$6 = wellKnownSymbol;
717 var wellKnownSymbolWrapped = {
721 var defineProperty = objectDefineProperty.f;
723 var defineWellKnownSymbol = function (NAME) {
724 var Symbol = path.Symbol || (path.Symbol = {});
725 if (!has(Symbol, NAME)) defineProperty(Symbol, NAME, {
726 value: wellKnownSymbolWrapped.f(NAME)
730 var defineProperty$1 = objectDefineProperty.f;
734 var TO_STRING_TAG = wellKnownSymbol('toStringTag');
736 var setToStringTag = function (it, TAG, STATIC) {
737 if (it && !has(it = STATIC ? it : it.prototype, TO_STRING_TAG)) {
738 defineProperty$1(it, TO_STRING_TAG, { configurable: true, value: TAG });
742 var aFunction$1 = function (it) {
743 if (typeof it != 'function') {
744 throw TypeError(String(it) + ' is not a function');
748 // optional / simple context binding
749 var functionBindContext = function (fn, that, length) {
751 if (that === undefined) return fn;
753 case 0: return function () {
754 return fn.call(that);
756 case 1: return function (a) {
757 return fn.call(that, a);
759 case 2: return function (a, b) {
760 return fn.call(that, a, b);
762 case 3: return function (a, b, c) {
763 return fn.call(that, a, b, c);
766 return function (/* ...args */) {
767 return fn.apply(that, arguments);
771 var SPECIES = wellKnownSymbol('species');
773 // `ArraySpeciesCreate` abstract operation
774 // https://tc39.github.io/ecma262/#sec-arrayspeciescreate
775 var arraySpeciesCreate = function (originalArray, length) {
777 if (isArray(originalArray)) {
778 C = originalArray.constructor;
779 // cross-realm fallback
780 if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;
781 else if (isObject(C)) {
783 if (C === null) C = undefined;
785 } return new (C === undefined ? Array : C)(length === 0 ? 0 : length);
790 // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex }` methods implementation
791 var createMethod$1 = function (TYPE) {
792 var IS_MAP = TYPE == 1;
793 var IS_FILTER = TYPE == 2;
794 var IS_SOME = TYPE == 3;
795 var IS_EVERY = TYPE == 4;
796 var IS_FIND_INDEX = TYPE == 6;
797 var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
798 return function ($this, callbackfn, that, specificCreate) {
799 var O = toObject($this);
800 var self = indexedObject(O);
801 var boundFunction = functionBindContext(callbackfn, that, 3);
802 var length = toLength(self.length);
804 var create = specificCreate || arraySpeciesCreate;
805 var target = IS_MAP ? create($this, length) : IS_FILTER ? create($this, 0) : undefined;
807 for (;length > index; index++) if (NO_HOLES || index in self) {
809 result = boundFunction(value, index, O);
811 if (IS_MAP) target[index] = result; // map
812 else if (result) switch (TYPE) {
813 case 3: return true; // some
814 case 5: return value; // find
815 case 6: return index; // findIndex
816 case 2: push.call(target, value); // filter
817 } else if (IS_EVERY) return false; // every
820 return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target;
824 var arrayIteration = {
825 // `Array.prototype.forEach` method
826 // https://tc39.github.io/ecma262/#sec-array.prototype.foreach
827 forEach: createMethod$1(0),
828 // `Array.prototype.map` method
829 // https://tc39.github.io/ecma262/#sec-array.prototype.map
830 map: createMethod$1(1),
831 // `Array.prototype.filter` method
832 // https://tc39.github.io/ecma262/#sec-array.prototype.filter
833 filter: createMethod$1(2),
834 // `Array.prototype.some` method
835 // https://tc39.github.io/ecma262/#sec-array.prototype.some
836 some: createMethod$1(3),
837 // `Array.prototype.every` method
838 // https://tc39.github.io/ecma262/#sec-array.prototype.every
839 every: createMethod$1(4),
840 // `Array.prototype.find` method
841 // https://tc39.github.io/ecma262/#sec-array.prototype.find
842 find: createMethod$1(5),
843 // `Array.prototype.findIndex` method
844 // https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
845 findIndex: createMethod$1(6)
848 var $forEach = arrayIteration.forEach;
850 var HIDDEN = sharedKey('hidden');
851 var SYMBOL = 'Symbol';
852 var PROTOTYPE$1 = 'prototype';
853 var TO_PRIMITIVE = wellKnownSymbol('toPrimitive');
854 var setInternalState = internalState.set;
855 var getInternalState = internalState.getterFor(SYMBOL);
856 var ObjectPrototype = Object[PROTOTYPE$1];
857 var $Symbol = global_1.Symbol;
858 var $stringify = getBuiltIn('JSON', 'stringify');
859 var nativeGetOwnPropertyDescriptor$1 = objectGetOwnPropertyDescriptor.f;
860 var nativeDefineProperty$1 = objectDefineProperty.f;
861 var nativeGetOwnPropertyNames$1 = objectGetOwnPropertyNamesExternal.f;
862 var nativePropertyIsEnumerable$1 = objectPropertyIsEnumerable.f;
863 var AllSymbols = shared('symbols');
864 var ObjectPrototypeSymbols = shared('op-symbols');
865 var StringToSymbolRegistry = shared('string-to-symbol-registry');
866 var SymbolToStringRegistry = shared('symbol-to-string-registry');
867 var WellKnownSymbolsStore$1 = shared('wks');
868 var QObject = global_1.QObject;
869 // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173
870 var USE_SETTER = !QObject || !QObject[PROTOTYPE$1] || !QObject[PROTOTYPE$1].findChild;
872 // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
873 var setSymbolDescriptor = descriptors && fails(function () {
874 return objectCreate(nativeDefineProperty$1({}, 'a', {
875 get: function () { return nativeDefineProperty$1(this, 'a', { value: 7 }).a; }
877 }) ? function (O, P, Attributes) {
878 var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$1(ObjectPrototype, P);
879 if (ObjectPrototypeDescriptor) delete ObjectPrototype[P];
880 nativeDefineProperty$1(O, P, Attributes);
881 if (ObjectPrototypeDescriptor && O !== ObjectPrototype) {
882 nativeDefineProperty$1(ObjectPrototype, P, ObjectPrototypeDescriptor);
884 } : nativeDefineProperty$1;
886 var wrap = function (tag, description) {
887 var symbol = AllSymbols[tag] = objectCreate($Symbol[PROTOTYPE$1]);
888 setInternalState(symbol, {
891 description: description
893 if (!descriptors) symbol.description = description;
897 var isSymbol = useSymbolAsUid ? function (it) {
898 return typeof it == 'symbol';
900 return Object(it) instanceof $Symbol;
903 var $defineProperty = function defineProperty(O, P, Attributes) {
904 if (O === ObjectPrototype) $defineProperty(ObjectPrototypeSymbols, P, Attributes);
906 var key = toPrimitive(P, true);
907 anObject(Attributes);
908 if (has(AllSymbols, key)) {
909 if (!Attributes.enumerable) {
910 if (!has(O, HIDDEN)) nativeDefineProperty$1(O, HIDDEN, createPropertyDescriptor(1, {}));
911 O[HIDDEN][key] = true;
913 if (has(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false;
914 Attributes = objectCreate(Attributes, { enumerable: createPropertyDescriptor(0, false) });
915 } return setSymbolDescriptor(O, key, Attributes);
916 } return nativeDefineProperty$1(O, key, Attributes);
919 var $defineProperties = function defineProperties(O, Properties) {
921 var properties = toIndexedObject(Properties);
922 var keys = objectKeys(properties).concat($getOwnPropertySymbols(properties));
923 $forEach(keys, function (key) {
924 if (!descriptors || $propertyIsEnumerable.call(properties, key)) $defineProperty(O, key, properties[key]);
929 var $create = function create(O, Properties) {
930 return Properties === undefined ? objectCreate(O) : $defineProperties(objectCreate(O), Properties);
933 var $propertyIsEnumerable = function propertyIsEnumerable(V) {
934 var P = toPrimitive(V, true);
935 var enumerable = nativePropertyIsEnumerable$1.call(this, P);
936 if (this === ObjectPrototype && has(AllSymbols, P) && !has(ObjectPrototypeSymbols, P)) return false;
937 return enumerable || !has(this, P) || !has(AllSymbols, P) || has(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true;
940 var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) {
941 var it = toIndexedObject(O);
942 var key = toPrimitive(P, true);
943 if (it === ObjectPrototype && has(AllSymbols, key) && !has(ObjectPrototypeSymbols, key)) return;
944 var descriptor = nativeGetOwnPropertyDescriptor$1(it, key);
945 if (descriptor && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) {
946 descriptor.enumerable = true;
951 var $getOwnPropertyNames = function getOwnPropertyNames(O) {
952 var names = nativeGetOwnPropertyNames$1(toIndexedObject(O));
954 $forEach(names, function (key) {
955 if (!has(AllSymbols, key) && !has(hiddenKeys, key)) result.push(key);
960 var $getOwnPropertySymbols = function getOwnPropertySymbols(O) {
961 var IS_OBJECT_PROTOTYPE = O === ObjectPrototype;
962 var names = nativeGetOwnPropertyNames$1(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject(O));
964 $forEach(names, function (key) {
965 if (has(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || has(ObjectPrototype, key))) {
966 result.push(AllSymbols[key]);
972 // `Symbol` constructor
973 // https://tc39.github.io/ecma262/#sec-symbol-constructor
975 $Symbol = function Symbol() {
976 if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor');
977 var description = !arguments.length || arguments[0] === undefined ? undefined : String(arguments[0]);
978 var tag = uid(description);
979 var setter = function (value) {
980 if (this === ObjectPrototype) setter.call(ObjectPrototypeSymbols, value);
981 if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;
982 setSymbolDescriptor(this, tag, createPropertyDescriptor(1, value));
984 if (descriptors && USE_SETTER) setSymbolDescriptor(ObjectPrototype, tag, { configurable: true, set: setter });
985 return wrap(tag, description);
988 redefine($Symbol[PROTOTYPE$1], 'toString', function toString() {
989 return getInternalState(this).tag;
992 redefine($Symbol, 'withoutSetter', function (description) {
993 return wrap(uid(description), description);
996 objectPropertyIsEnumerable.f = $propertyIsEnumerable;
997 objectDefineProperty.f = $defineProperty;
998 objectGetOwnPropertyDescriptor.f = $getOwnPropertyDescriptor;
999 objectGetOwnPropertyNames.f = objectGetOwnPropertyNamesExternal.f = $getOwnPropertyNames;
1000 objectGetOwnPropertySymbols.f = $getOwnPropertySymbols;
1002 wellKnownSymbolWrapped.f = function (name) {
1003 return wrap(wellKnownSymbol(name), name);
1007 // https://github.com/tc39/proposal-Symbol-description
1008 nativeDefineProperty$1($Symbol[PROTOTYPE$1], 'description', {
1010 get: function description() {
1011 return getInternalState(this).description;
1015 redefine(ObjectPrototype, 'propertyIsEnumerable', $propertyIsEnumerable, { unsafe: true });
1020 _export({ global: true, wrap: true, forced: !nativeSymbol, sham: !nativeSymbol }, {
1024 $forEach(objectKeys(WellKnownSymbolsStore$1), function (name) {
1025 defineWellKnownSymbol(name);
1028 _export({ target: SYMBOL, stat: true, forced: !nativeSymbol }, {
1029 // `Symbol.for` method
1030 // https://tc39.github.io/ecma262/#sec-symbol.for
1031 'for': function (key) {
1032 var string = String(key);
1033 if (has(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string];
1034 var symbol = $Symbol(string);
1035 StringToSymbolRegistry[string] = symbol;
1036 SymbolToStringRegistry[symbol] = string;
1039 // `Symbol.keyFor` method
1040 // https://tc39.github.io/ecma262/#sec-symbol.keyfor
1041 keyFor: function keyFor(sym) {
1042 if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol');
1043 if (has(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym];
1045 useSetter: function () { USE_SETTER = true; },
1046 useSimple: function () { USE_SETTER = false; }
1049 _export({ target: 'Object', stat: true, forced: !nativeSymbol, sham: !descriptors }, {
1050 // `Object.create` method
1051 // https://tc39.github.io/ecma262/#sec-object.create
1053 // `Object.defineProperty` method
1054 // https://tc39.github.io/ecma262/#sec-object.defineproperty
1055 defineProperty: $defineProperty,
1056 // `Object.defineProperties` method
1057 // https://tc39.github.io/ecma262/#sec-object.defineproperties
1058 defineProperties: $defineProperties,
1059 // `Object.getOwnPropertyDescriptor` method
1060 // https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptors
1061 getOwnPropertyDescriptor: $getOwnPropertyDescriptor
1064 _export({ target: 'Object', stat: true, forced: !nativeSymbol }, {
1065 // `Object.getOwnPropertyNames` method
1066 // https://tc39.github.io/ecma262/#sec-object.getownpropertynames
1067 getOwnPropertyNames: $getOwnPropertyNames,
1068 // `Object.getOwnPropertySymbols` method
1069 // https://tc39.github.io/ecma262/#sec-object.getownpropertysymbols
1070 getOwnPropertySymbols: $getOwnPropertySymbols
1073 // Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives
1074 // https://bugs.chromium.org/p/v8/issues/detail?id=3443
1075 _export({ target: 'Object', stat: true, forced: fails(function () { objectGetOwnPropertySymbols.f(1); }) }, {
1076 getOwnPropertySymbols: function getOwnPropertySymbols(it) {
1077 return objectGetOwnPropertySymbols.f(toObject(it));
1081 // `JSON.stringify` method behavior with symbols
1082 // https://tc39.github.io/ecma262/#sec-json.stringify
1084 var FORCED_JSON_STRINGIFY = !nativeSymbol || fails(function () {
1085 var symbol = $Symbol();
1086 // MS Edge converts symbol values to JSON as {}
1087 return $stringify([symbol]) != '[null]'
1088 // WebKit converts symbol values to JSON as null
1089 || $stringify({ a: symbol }) != '{}'
1090 // V8 throws on boxed symbols
1091 || $stringify(Object(symbol)) != '{}';
1094 _export({ target: 'JSON', stat: true, forced: FORCED_JSON_STRINGIFY }, {
1095 // eslint-disable-next-line no-unused-vars
1096 stringify: function stringify(it, replacer, space) {
1100 while (arguments.length > index) args.push(arguments[index++]);
1101 $replacer = replacer;
1102 if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined
1103 if (!isArray(replacer)) replacer = function (key, value) {
1104 if (typeof $replacer == 'function') value = $replacer.call(this, key, value);
1105 if (!isSymbol(value)) return value;
1108 return $stringify.apply(null, args);
1113 // `Symbol.prototype[@@toPrimitive]` method
1114 // https://tc39.github.io/ecma262/#sec-symbol.prototype-@@toprimitive
1115 if (!$Symbol[PROTOTYPE$1][TO_PRIMITIVE]) {
1116 createNonEnumerableProperty($Symbol[PROTOTYPE$1], TO_PRIMITIVE, $Symbol[PROTOTYPE$1].valueOf);
1118 // `Symbol.prototype[@@toStringTag]` property
1119 // https://tc39.github.io/ecma262/#sec-symbol.prototype-@@tostringtag
1120 setToStringTag($Symbol, SYMBOL);
1122 hiddenKeys[HIDDEN] = true;
1124 var defineProperty$2 = objectDefineProperty.f;
1127 var NativeSymbol = global_1.Symbol;
1129 if (descriptors && typeof NativeSymbol == 'function' && (!('description' in NativeSymbol.prototype) ||
1131 NativeSymbol().description !== undefined
1133 var EmptyStringDescriptionStore = {};
1134 // wrap Symbol constructor for correct work with undefined description
1135 var SymbolWrapper = function Symbol() {
1136 var description = arguments.length < 1 || arguments[0] === undefined ? undefined : String(arguments[0]);
1137 var result = this instanceof SymbolWrapper
1138 ? new NativeSymbol(description)
1139 // in Edge 13, String(Symbol(undefined)) === 'Symbol(undefined)'
1140 : description === undefined ? NativeSymbol() : NativeSymbol(description);
1141 if (description === '') EmptyStringDescriptionStore[result] = true;
1144 copyConstructorProperties(SymbolWrapper, NativeSymbol);
1145 var symbolPrototype = SymbolWrapper.prototype = NativeSymbol.prototype;
1146 symbolPrototype.constructor = SymbolWrapper;
1148 var symbolToString = symbolPrototype.toString;
1149 var native = String(NativeSymbol('test')) == 'Symbol(test)';
1150 var regexp = /^Symbol\((.*)\)[^)]+$/;
1151 defineProperty$2(symbolPrototype, 'description', {
1153 get: function description() {
1154 var symbol = isObject(this) ? this.valueOf() : this;
1155 var string = symbolToString.call(symbol);
1156 if (has(EmptyStringDescriptionStore, symbol)) return '';
1157 var desc = native ? string.slice(7, -1) : string.replace(regexp, '$1');
1158 return desc === '' ? undefined : desc;
1162 _export({ global: true, forced: true }, {
1163 Symbol: SymbolWrapper
1167 // `Symbol.iterator` well-known symbol
1168 // https://tc39.github.io/ecma262/#sec-symbol.iterator
1169 defineWellKnownSymbol('iterator');
1171 var arrayMethodIsStrict = function (METHOD_NAME, argument) {
1172 var method = [][METHOD_NAME];
1173 return !!method && fails(function () {
1174 // eslint-disable-next-line no-useless-call,no-throw-literal
1175 method.call(null, argument || function () { throw 1; }, 1);
1179 var defineProperty$3 = Object.defineProperty;
1182 var thrower = function (it) { throw it; };
1184 var arrayMethodUsesToLength = function (METHOD_NAME, options) {
1185 if (has(cache, METHOD_NAME)) return cache[METHOD_NAME];
1186 if (!options) options = {};
1187 var method = [][METHOD_NAME];
1188 var ACCESSORS = has(options, 'ACCESSORS') ? options.ACCESSORS : false;
1189 var argument0 = has(options, 0) ? options[0] : thrower;
1190 var argument1 = has(options, 1) ? options[1] : undefined;
1192 return cache[METHOD_NAME] = !!method && !fails(function () {
1193 if (ACCESSORS && !descriptors) return true;
1194 var O = { length: -1 };
1196 if (ACCESSORS) defineProperty$3(O, 1, { enumerable: true, get: thrower });
1199 method.call(O, argument0, argument1);
1203 var $forEach$1 = arrayIteration.forEach;
1207 var STRICT_METHOD = arrayMethodIsStrict('forEach');
1208 var USES_TO_LENGTH = arrayMethodUsesToLength('forEach');
1210 // `Array.prototype.forEach` method implementation
1211 // https://tc39.github.io/ecma262/#sec-array.prototype.foreach
1212 var arrayForEach = (!STRICT_METHOD || !USES_TO_LENGTH) ? function forEach(callbackfn /* , thisArg */) {
1213 return $forEach$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
1216 // `Array.prototype.forEach` method
1217 // https://tc39.github.io/ecma262/#sec-array.prototype.foreach
1218 _export({ target: 'Array', proto: true, forced: [].forEach != arrayForEach }, {
1219 forEach: arrayForEach
1222 var $indexOf = arrayIncludes.indexOf;
1226 var nativeIndexOf = [].indexOf;
1228 var NEGATIVE_ZERO = !!nativeIndexOf && 1 / [1].indexOf(1, -0) < 0;
1229 var STRICT_METHOD$1 = arrayMethodIsStrict('indexOf');
1230 var USES_TO_LENGTH$1 = arrayMethodUsesToLength('indexOf', { ACCESSORS: true, 1: 0 });
1232 // `Array.prototype.indexOf` method
1233 // https://tc39.github.io/ecma262/#sec-array.prototype.indexof
1234 _export({ target: 'Array', proto: true, forced: NEGATIVE_ZERO || !STRICT_METHOD$1 || !USES_TO_LENGTH$1 }, {
1235 indexOf: function indexOf(searchElement /* , fromIndex = 0 */) {
1236 return NEGATIVE_ZERO
1238 ? nativeIndexOf.apply(this, arguments) || 0
1239 : $indexOf(this, searchElement, arguments.length > 1 ? arguments[1] : undefined);
1243 // `Array.isArray` method
1244 // https://tc39.github.io/ecma262/#sec-array.isarray
1245 _export({ target: 'Array', stat: true }, {
1249 var UNSCOPABLES = wellKnownSymbol('unscopables');
1250 var ArrayPrototype = Array.prototype;
1252 // Array.prototype[@@unscopables]
1253 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
1254 if (ArrayPrototype[UNSCOPABLES] == undefined) {
1255 objectDefineProperty.f(ArrayPrototype, UNSCOPABLES, {
1257 value: objectCreate(null)
1261 // add a key to Array.prototype[@@unscopables]
1262 var addToUnscopables = function (key) {
1263 ArrayPrototype[UNSCOPABLES][key] = true;
1268 var correctPrototypeGetter = !fails(function () {
1269 function F() { /* empty */ }
1270 F.prototype.constructor = null;
1271 return Object.getPrototypeOf(new F()) !== F.prototype;
1274 var IE_PROTO$1 = sharedKey('IE_PROTO');
1275 var ObjectPrototype$1 = Object.prototype;
1277 // `Object.getPrototypeOf` method
1278 // https://tc39.github.io/ecma262/#sec-object.getprototypeof
1279 var objectGetPrototypeOf = correctPrototypeGetter ? Object.getPrototypeOf : function (O) {
1281 if (has(O, IE_PROTO$1)) return O[IE_PROTO$1];
1282 if (typeof O.constructor == 'function' && O instanceof O.constructor) {
1283 return O.constructor.prototype;
1284 } return O instanceof Object ? ObjectPrototype$1 : null;
1287 var ITERATOR = wellKnownSymbol('iterator');
1288 var BUGGY_SAFARI_ITERATORS = false;
1290 var returnThis = function () { return this; };
1292 // `%IteratorPrototype%` object
1293 // https://tc39.github.io/ecma262/#sec-%iteratorprototype%-object
1294 var IteratorPrototype, PrototypeOfArrayIteratorPrototype, arrayIterator;
1297 arrayIterator = [].keys();
1298 // Safari 8 has buggy iterators w/o `next`
1299 if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS = true;
1301 PrototypeOfArrayIteratorPrototype = objectGetPrototypeOf(objectGetPrototypeOf(arrayIterator));
1302 if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype = PrototypeOfArrayIteratorPrototype;
1306 if (IteratorPrototype == undefined) IteratorPrototype = {};
1308 // 25.1.2.1.1 %IteratorPrototype%[@@iterator]()
1309 if ( !has(IteratorPrototype, ITERATOR)) {
1310 createNonEnumerableProperty(IteratorPrototype, ITERATOR, returnThis);
1313 var iteratorsCore = {
1314 IteratorPrototype: IteratorPrototype,
1315 BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS
1318 var IteratorPrototype$1 = iteratorsCore.IteratorPrototype;
1324 var returnThis$1 = function () { return this; };
1326 var createIteratorConstructor = function (IteratorConstructor, NAME, next) {
1327 var TO_STRING_TAG = NAME + ' Iterator';
1328 IteratorConstructor.prototype = objectCreate(IteratorPrototype$1, { next: createPropertyDescriptor(1, next) });
1329 setToStringTag(IteratorConstructor, TO_STRING_TAG, false);
1330 iterators[TO_STRING_TAG] = returnThis$1;
1331 return IteratorConstructor;
1334 var aPossiblePrototype = function (it) {
1335 if (!isObject(it) && it !== null) {
1336 throw TypeError("Can't set " + String(it) + ' as a prototype');
1340 // `Object.setPrototypeOf` method
1341 // https://tc39.github.io/ecma262/#sec-object.setprototypeof
1342 // Works with __proto__ only. Old v8 can't work with null proto objects.
1343 /* eslint-disable no-proto */
1344 var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () {
1345 var CORRECT_SETTER = false;
1349 setter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set;
1350 setter.call(test, []);
1351 CORRECT_SETTER = test instanceof Array;
1352 } catch (error) { /* empty */ }
1353 return function setPrototypeOf(O, proto) {
1355 aPossiblePrototype(proto);
1356 if (CORRECT_SETTER) setter.call(O, proto);
1357 else O.__proto__ = proto;
1362 var IteratorPrototype$2 = iteratorsCore.IteratorPrototype;
1363 var BUGGY_SAFARI_ITERATORS$1 = iteratorsCore.BUGGY_SAFARI_ITERATORS;
1364 var ITERATOR$1 = wellKnownSymbol('iterator');
1366 var VALUES = 'values';
1367 var ENTRIES = 'entries';
1369 var returnThis$2 = function () { return this; };
1371 var defineIterator = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) {
1372 createIteratorConstructor(IteratorConstructor, NAME, next);
1374 var getIterationMethod = function (KIND) {
1375 if (KIND === DEFAULT && defaultIterator) return defaultIterator;
1376 if (!BUGGY_SAFARI_ITERATORS$1 && KIND in IterablePrototype) return IterablePrototype[KIND];
1378 case KEYS: return function keys() { return new IteratorConstructor(this, KIND); };
1379 case VALUES: return function values() { return new IteratorConstructor(this, KIND); };
1380 case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); };
1381 } return function () { return new IteratorConstructor(this); };
1384 var TO_STRING_TAG = NAME + ' Iterator';
1385 var INCORRECT_VALUES_NAME = false;
1386 var IterablePrototype = Iterable.prototype;
1387 var nativeIterator = IterablePrototype[ITERATOR$1]
1388 || IterablePrototype['@@iterator']
1389 || DEFAULT && IterablePrototype[DEFAULT];
1390 var defaultIterator = !BUGGY_SAFARI_ITERATORS$1 && nativeIterator || getIterationMethod(DEFAULT);
1391 var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator;
1392 var CurrentIteratorPrototype, methods, KEY;
1395 if (anyNativeIterator) {
1396 CurrentIteratorPrototype = objectGetPrototypeOf(anyNativeIterator.call(new Iterable()));
1397 if (IteratorPrototype$2 !== Object.prototype && CurrentIteratorPrototype.next) {
1398 if ( objectGetPrototypeOf(CurrentIteratorPrototype) !== IteratorPrototype$2) {
1399 if (objectSetPrototypeOf) {
1400 objectSetPrototypeOf(CurrentIteratorPrototype, IteratorPrototype$2);
1401 } else if (typeof CurrentIteratorPrototype[ITERATOR$1] != 'function') {
1402 createNonEnumerableProperty(CurrentIteratorPrototype, ITERATOR$1, returnThis$2);
1405 // Set @@toStringTag to native iterators
1406 setToStringTag(CurrentIteratorPrototype, TO_STRING_TAG, true);
1410 // fix Array#{values, @@iterator}.name in V8 / FF
1411 if (DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) {
1412 INCORRECT_VALUES_NAME = true;
1413 defaultIterator = function values() { return nativeIterator.call(this); };
1417 if ( IterablePrototype[ITERATOR$1] !== defaultIterator) {
1418 createNonEnumerableProperty(IterablePrototype, ITERATOR$1, defaultIterator);
1420 iterators[NAME] = defaultIterator;
1422 // export additional methods
1425 values: getIterationMethod(VALUES),
1426 keys: IS_SET ? defaultIterator : getIterationMethod(KEYS),
1427 entries: getIterationMethod(ENTRIES)
1429 if (FORCED) for (KEY in methods) {
1430 if (BUGGY_SAFARI_ITERATORS$1 || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) {
1431 redefine(IterablePrototype, KEY, methods[KEY]);
1433 } else _export({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS$1 || INCORRECT_VALUES_NAME }, methods);
1439 var ARRAY_ITERATOR = 'Array Iterator';
1440 var setInternalState$1 = internalState.set;
1441 var getInternalState$1 = internalState.getterFor(ARRAY_ITERATOR);
1443 // `Array.prototype.entries` method
1444 // https://tc39.github.io/ecma262/#sec-array.prototype.entries
1445 // `Array.prototype.keys` method
1446 // https://tc39.github.io/ecma262/#sec-array.prototype.keys
1447 // `Array.prototype.values` method
1448 // https://tc39.github.io/ecma262/#sec-array.prototype.values
1449 // `Array.prototype[@@iterator]` method
1450 // https://tc39.github.io/ecma262/#sec-array.prototype-@@iterator
1451 // `CreateArrayIterator` internal method
1452 // https://tc39.github.io/ecma262/#sec-createarrayiterator
1453 var es_array_iterator = defineIterator(Array, 'Array', function (iterated, kind) {
1454 setInternalState$1(this, {
1455 type: ARRAY_ITERATOR,
1456 target: toIndexedObject(iterated), // target
1457 index: 0, // next index
1460 // `%ArrayIteratorPrototype%.next` method
1461 // https://tc39.github.io/ecma262/#sec-%arrayiteratorprototype%.next
1463 var state = getInternalState$1(this);
1464 var target = state.target;
1465 var kind = state.kind;
1466 var index = state.index++;
1467 if (!target || index >= target.length) {
1468 state.target = undefined;
1469 return { value: undefined, done: true };
1471 if (kind == 'keys') return { value: index, done: false };
1472 if (kind == 'values') return { value: target[index], done: false };
1473 return { value: [index, target[index]], done: false };
1476 // argumentsList[@@iterator] is %ArrayProto_values%
1477 // https://tc39.github.io/ecma262/#sec-createunmappedargumentsobject
1478 // https://tc39.github.io/ecma262/#sec-createmappedargumentsobject
1479 iterators.Arguments = iterators.Array;
1481 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
1482 addToUnscopables('keys');
1483 addToUnscopables('values');
1484 addToUnscopables('entries');
1486 var nativeJoin = [].join;
1488 var ES3_STRINGS = indexedObject != Object;
1489 var STRICT_METHOD$2 = arrayMethodIsStrict('join', ',');
1491 // `Array.prototype.join` method
1492 // https://tc39.github.io/ecma262/#sec-array.prototype.join
1493 _export({ target: 'Array', proto: true, forced: ES3_STRINGS || !STRICT_METHOD$2 }, {
1494 join: function join(separator) {
1495 return nativeJoin.call(toIndexedObject(this), separator === undefined ? ',' : separator);
1499 var engineUserAgent = getBuiltIn('navigator', 'userAgent') || '';
1501 var process$1 = global_1.process;
1502 var versions = process$1 && process$1.versions;
1503 var v8 = versions && versions.v8;
1507 match = v8.split('.');
1508 version = match[0] + match[1];
1509 } else if (engineUserAgent) {
1510 match = engineUserAgent.match(/Edge\/(\d+)/);
1511 if (!match || match[1] >= 74) {
1512 match = engineUserAgent.match(/Chrome\/(\d+)/);
1513 if (match) version = match[1];
1517 var engineV8Version = version && +version;
1519 var SPECIES$1 = wellKnownSymbol('species');
1521 var arrayMethodHasSpeciesSupport = function (METHOD_NAME) {
1522 // We can't use this feature detection in V8 since it causes
1523 // deoptimization and serious performance degradation
1524 // https://github.com/zloirock/core-js/issues/677
1525 return engineV8Version >= 51 || !fails(function () {
1527 var constructor = array.constructor = {};
1528 constructor[SPECIES$1] = function () {
1531 return array[METHOD_NAME](Boolean).foo !== 1;
1535 var $map = arrayIteration.map;
1539 var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('map');
1541 var USES_TO_LENGTH$2 = arrayMethodUsesToLength('map');
1543 // `Array.prototype.map` method
1544 // https://tc39.github.io/ecma262/#sec-array.prototype.map
1545 // with adding support of @@species
1546 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT || !USES_TO_LENGTH$2 }, {
1547 map: function map(callbackfn /* , thisArg */) {
1548 return $map(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
1552 var createProperty = function (object, key, value) {
1553 var propertyKey = toPrimitive(key);
1554 if (propertyKey in object) objectDefineProperty.f(object, propertyKey, createPropertyDescriptor(0, value));
1555 else object[propertyKey] = value;
1558 var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport('slice');
1559 var USES_TO_LENGTH$3 = arrayMethodUsesToLength('slice', { ACCESSORS: true, 0: 0, 1: 2 });
1561 var SPECIES$2 = wellKnownSymbol('species');
1562 var nativeSlice = [].slice;
1563 var max$1 = Math.max;
1565 // `Array.prototype.slice` method
1566 // https://tc39.github.io/ecma262/#sec-array.prototype.slice
1567 // fallback for not array-like ES3 strings and DOM objects
1568 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 || !USES_TO_LENGTH$3 }, {
1569 slice: function slice(start, end) {
1570 var O = toIndexedObject(this);
1571 var length = toLength(O.length);
1572 var k = toAbsoluteIndex(start, length);
1573 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
1574 // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible
1575 var Constructor, result, n;
1577 Constructor = O.constructor;
1578 // cross-realm fallback
1579 if (typeof Constructor == 'function' && (Constructor === Array || isArray(Constructor.prototype))) {
1580 Constructor = undefined;
1581 } else if (isObject(Constructor)) {
1582 Constructor = Constructor[SPECIES$2];
1583 if (Constructor === null) Constructor = undefined;
1585 if (Constructor === Array || Constructor === undefined) {
1586 return nativeSlice.call(O, k, fin);
1589 result = new (Constructor === undefined ? Array : Constructor)(max$1(fin - k, 0));
1590 for (n = 0; k < fin; k++, n++) if (k in O) createProperty(result, n, O[k]);
1596 var arrayBufferNative = typeof ArrayBuffer !== 'undefined' && typeof DataView !== 'undefined';
1598 var redefineAll = function (target, src, options) {
1599 for (var key in src) redefine(target, key, src[key], options);
1603 var anInstance = function (it, Constructor, name) {
1604 if (!(it instanceof Constructor)) {
1605 throw TypeError('Incorrect ' + (name ? name + ' ' : '') + 'invocation');
1609 // `ToIndex` abstract operation
1610 // https://tc39.github.io/ecma262/#sec-toindex
1611 var toIndex = function (it) {
1612 if (it === undefined) return 0;
1613 var number = toInteger(it);
1614 var length = toLength(number);
1615 if (number !== length) throw RangeError('Wrong length or index');
1619 // IEEE754 conversions based on https://github.com/feross/ieee754
1620 // eslint-disable-next-line no-shadow-restricted-names
1621 var Infinity$1 = 1 / 0;
1624 var floor$1 = Math.floor;
1628 var pack = function (number, mantissaLength, bytes) {
1629 var buffer = new Array(bytes);
1630 var exponentLength = bytes * 8 - mantissaLength - 1;
1631 var eMax = (1 << exponentLength) - 1;
1632 var eBias = eMax >> 1;
1633 var rt = mantissaLength === 23 ? pow(2, -24) - pow(2, -77) : 0;
1634 var sign = number < 0 || number === 0 && 1 / number < 0 ? 1 : 0;
1636 var exponent, mantissa, c;
1637 number = abs(number);
1638 // eslint-disable-next-line no-self-compare
1639 if (number != number || number === Infinity$1) {
1640 // eslint-disable-next-line no-self-compare
1641 mantissa = number != number ? 1 : 0;
1644 exponent = floor$1(log(number) / LN2);
1645 if (number * (c = pow(2, -exponent)) < 1) {
1649 if (exponent + eBias >= 1) {
1652 number += rt * pow(2, 1 - eBias);
1654 if (number * c >= 2) {
1658 if (exponent + eBias >= eMax) {
1661 } else if (exponent + eBias >= 1) {
1662 mantissa = (number * c - 1) * pow(2, mantissaLength);
1663 exponent = exponent + eBias;
1665 mantissa = number * pow(2, eBias - 1) * pow(2, mantissaLength);
1669 for (; mantissaLength >= 8; buffer[index++] = mantissa & 255, mantissa /= 256, mantissaLength -= 8);
1670 exponent = exponent << mantissaLength | mantissa;
1671 exponentLength += mantissaLength;
1672 for (; exponentLength > 0; buffer[index++] = exponent & 255, exponent /= 256, exponentLength -= 8);
1673 buffer[--index] |= sign * 128;
1677 var unpack = function (buffer, mantissaLength) {
1678 var bytes = buffer.length;
1679 var exponentLength = bytes * 8 - mantissaLength - 1;
1680 var eMax = (1 << exponentLength) - 1;
1681 var eBias = eMax >> 1;
1682 var nBits = exponentLength - 7;
1683 var index = bytes - 1;
1684 var sign = buffer[index--];
1685 var exponent = sign & 127;
1688 for (; nBits > 0; exponent = exponent * 256 + buffer[index], index--, nBits -= 8);
1689 mantissa = exponent & (1 << -nBits) - 1;
1690 exponent >>= -nBits;
1691 nBits += mantissaLength;
1692 for (; nBits > 0; mantissa = mantissa * 256 + buffer[index], index--, nBits -= 8);
1693 if (exponent === 0) {
1694 exponent = 1 - eBias;
1695 } else if (exponent === eMax) {
1696 return mantissa ? NaN : sign ? -Infinity$1 : Infinity$1;
1698 mantissa = mantissa + pow(2, mantissaLength);
1699 exponent = exponent - eBias;
1700 } return (sign ? -1 : 1) * mantissa * pow(2, exponent - mantissaLength);
1708 // `Array.prototype.fill` method implementation
1709 // https://tc39.github.io/ecma262/#sec-array.prototype.fill
1710 var arrayFill = function fill(value /* , start = 0, end = @length */) {
1711 var O = toObject(this);
1712 var length = toLength(O.length);
1713 var argumentsLength = arguments.length;
1714 var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length);
1715 var end = argumentsLength > 2 ? arguments[2] : undefined;
1716 var endPos = end === undefined ? length : toAbsoluteIndex(end, length);
1717 while (endPos > index) O[index++] = value;
1721 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
1722 var defineProperty$4 = objectDefineProperty.f;
1727 var getInternalState$2 = internalState.get;
1728 var setInternalState$2 = internalState.set;
1729 var ARRAY_BUFFER = 'ArrayBuffer';
1730 var DATA_VIEW = 'DataView';
1731 var PROTOTYPE$2 = 'prototype';
1732 var WRONG_LENGTH = 'Wrong length';
1733 var WRONG_INDEX = 'Wrong index';
1734 var NativeArrayBuffer = global_1[ARRAY_BUFFER];
1735 var $ArrayBuffer = NativeArrayBuffer;
1736 var $DataView = global_1[DATA_VIEW];
1737 var $DataViewPrototype = $DataView && $DataView[PROTOTYPE$2];
1738 var ObjectPrototype$2 = Object.prototype;
1739 var RangeError$1 = global_1.RangeError;
1741 var packIEEE754 = ieee754.pack;
1742 var unpackIEEE754 = ieee754.unpack;
1744 var packInt8 = function (number) {
1745 return [number & 0xFF];
1748 var packInt16 = function (number) {
1749 return [number & 0xFF, number >> 8 & 0xFF];
1752 var packInt32 = function (number) {
1753 return [number & 0xFF, number >> 8 & 0xFF, number >> 16 & 0xFF, number >> 24 & 0xFF];
1756 var unpackInt32 = function (buffer) {
1757 return buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0];
1760 var packFloat32 = function (number) {
1761 return packIEEE754(number, 23, 4);
1764 var packFloat64 = function (number) {
1765 return packIEEE754(number, 52, 8);
1768 var addGetter = function (Constructor, key) {
1769 defineProperty$4(Constructor[PROTOTYPE$2], key, { get: function () { return getInternalState$2(this)[key]; } });
1772 var get$1 = function (view, count, index, isLittleEndian) {
1773 var intIndex = toIndex(index);
1774 var store = getInternalState$2(view);
1775 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1776 var bytes = getInternalState$2(store.buffer).bytes;
1777 var start = intIndex + store.byteOffset;
1778 var pack = bytes.slice(start, start + count);
1779 return isLittleEndian ? pack : pack.reverse();
1782 var set$1 = function (view, count, index, conversion, value, isLittleEndian) {
1783 var intIndex = toIndex(index);
1784 var store = getInternalState$2(view);
1785 if (intIndex + count > store.byteLength) throw RangeError$1(WRONG_INDEX);
1786 var bytes = getInternalState$2(store.buffer).bytes;
1787 var start = intIndex + store.byteOffset;
1788 var pack = conversion(+value);
1789 for (var i = 0; i < count; i++) bytes[start + i] = pack[isLittleEndian ? i : count - i - 1];
1792 if (!arrayBufferNative) {
1793 $ArrayBuffer = function ArrayBuffer(length) {
1794 anInstance(this, $ArrayBuffer, ARRAY_BUFFER);
1795 var byteLength = toIndex(length);
1796 setInternalState$2(this, {
1797 bytes: arrayFill.call(new Array(byteLength), 0),
1798 byteLength: byteLength
1800 if (!descriptors) this.byteLength = byteLength;
1803 $DataView = function DataView(buffer, byteOffset, byteLength) {
1804 anInstance(this, $DataView, DATA_VIEW);
1805 anInstance(buffer, $ArrayBuffer, DATA_VIEW);
1806 var bufferLength = getInternalState$2(buffer).byteLength;
1807 var offset = toInteger(byteOffset);
1808 if (offset < 0 || offset > bufferLength) throw RangeError$1('Wrong offset');
1809 byteLength = byteLength === undefined ? bufferLength - offset : toLength(byteLength);
1810 if (offset + byteLength > bufferLength) throw RangeError$1(WRONG_LENGTH);
1811 setInternalState$2(this, {
1813 byteLength: byteLength,
1817 this.buffer = buffer;
1818 this.byteLength = byteLength;
1819 this.byteOffset = offset;
1824 addGetter($ArrayBuffer, 'byteLength');
1825 addGetter($DataView, 'buffer');
1826 addGetter($DataView, 'byteLength');
1827 addGetter($DataView, 'byteOffset');
1830 redefineAll($DataView[PROTOTYPE$2], {
1831 getInt8: function getInt8(byteOffset) {
1832 return get$1(this, 1, byteOffset)[0] << 24 >> 24;
1834 getUint8: function getUint8(byteOffset) {
1835 return get$1(this, 1, byteOffset)[0];
1837 getInt16: function getInt16(byteOffset /* , littleEndian */) {
1838 var bytes = get$1(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
1839 return (bytes[1] << 8 | bytes[0]) << 16 >> 16;
1841 getUint16: function getUint16(byteOffset /* , littleEndian */) {
1842 var bytes = get$1(this, 2, byteOffset, arguments.length > 1 ? arguments[1] : undefined);
1843 return bytes[1] << 8 | bytes[0];
1845 getInt32: function getInt32(byteOffset /* , littleEndian */) {
1846 return unpackInt32(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined));
1848 getUint32: function getUint32(byteOffset /* , littleEndian */) {
1849 return unpackInt32(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined)) >>> 0;
1851 getFloat32: function getFloat32(byteOffset /* , littleEndian */) {
1852 return unpackIEEE754(get$1(this, 4, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 23);
1854 getFloat64: function getFloat64(byteOffset /* , littleEndian */) {
1855 return unpackIEEE754(get$1(this, 8, byteOffset, arguments.length > 1 ? arguments[1] : undefined), 52);
1857 setInt8: function setInt8(byteOffset, value) {
1858 set$1(this, 1, byteOffset, packInt8, value);
1860 setUint8: function setUint8(byteOffset, value) {
1861 set$1(this, 1, byteOffset, packInt8, value);
1863 setInt16: function setInt16(byteOffset, value /* , littleEndian */) {
1864 set$1(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
1866 setUint16: function setUint16(byteOffset, value /* , littleEndian */) {
1867 set$1(this, 2, byteOffset, packInt16, value, arguments.length > 2 ? arguments[2] : undefined);
1869 setInt32: function setInt32(byteOffset, value /* , littleEndian */) {
1870 set$1(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
1872 setUint32: function setUint32(byteOffset, value /* , littleEndian */) {
1873 set$1(this, 4, byteOffset, packInt32, value, arguments.length > 2 ? arguments[2] : undefined);
1875 setFloat32: function setFloat32(byteOffset, value /* , littleEndian */) {
1876 set$1(this, 4, byteOffset, packFloat32, value, arguments.length > 2 ? arguments[2] : undefined);
1878 setFloat64: function setFloat64(byteOffset, value /* , littleEndian */) {
1879 set$1(this, 8, byteOffset, packFloat64, value, arguments.length > 2 ? arguments[2] : undefined);
1883 if (!fails(function () {
1884 NativeArrayBuffer(1);
1885 }) || !fails(function () {
1886 new NativeArrayBuffer(-1); // eslint-disable-line no-new
1887 }) || fails(function () {
1888 new NativeArrayBuffer(); // eslint-disable-line no-new
1889 new NativeArrayBuffer(1.5); // eslint-disable-line no-new
1890 new NativeArrayBuffer(NaN); // eslint-disable-line no-new
1891 return NativeArrayBuffer.name != ARRAY_BUFFER;
1893 $ArrayBuffer = function ArrayBuffer(length) {
1894 anInstance(this, $ArrayBuffer);
1895 return new NativeArrayBuffer(toIndex(length));
1897 var ArrayBufferPrototype = $ArrayBuffer[PROTOTYPE$2] = NativeArrayBuffer[PROTOTYPE$2];
1898 for (var keys$1 = getOwnPropertyNames(NativeArrayBuffer), j = 0, key; keys$1.length > j;) {
1899 if (!((key = keys$1[j++]) in $ArrayBuffer)) {
1900 createNonEnumerableProperty($ArrayBuffer, key, NativeArrayBuffer[key]);
1903 ArrayBufferPrototype.constructor = $ArrayBuffer;
1906 // WebKit bug - the same parent prototype for typed arrays and data view
1907 if (objectSetPrototypeOf && objectGetPrototypeOf($DataViewPrototype) !== ObjectPrototype$2) {
1908 objectSetPrototypeOf($DataViewPrototype, ObjectPrototype$2);
1911 // iOS Safari 7.x bug
1912 var testView = new $DataView(new $ArrayBuffer(2));
1913 var nativeSetInt8 = $DataViewPrototype.setInt8;
1914 testView.setInt8(0, 2147483648);
1915 testView.setInt8(1, 2147483649);
1916 if (testView.getInt8(0) || !testView.getInt8(1)) redefineAll($DataViewPrototype, {
1917 setInt8: function setInt8(byteOffset, value) {
1918 nativeSetInt8.call(this, byteOffset, value << 24 >> 24);
1920 setUint8: function setUint8(byteOffset, value) {
1921 nativeSetInt8.call(this, byteOffset, value << 24 >> 24);
1923 }, { unsafe: true });
1926 setToStringTag($ArrayBuffer, ARRAY_BUFFER);
1927 setToStringTag($DataView, DATA_VIEW);
1930 ArrayBuffer: $ArrayBuffer,
1934 var SPECIES$3 = wellKnownSymbol('species');
1936 var setSpecies = function (CONSTRUCTOR_NAME) {
1937 var Constructor = getBuiltIn(CONSTRUCTOR_NAME);
1938 var defineProperty = objectDefineProperty.f;
1940 if (descriptors && Constructor && !Constructor[SPECIES$3]) {
1941 defineProperty(Constructor, SPECIES$3, {
1943 get: function () { return this; }
1948 var ARRAY_BUFFER$1 = 'ArrayBuffer';
1949 var ArrayBuffer$1 = arrayBuffer[ARRAY_BUFFER$1];
1950 var NativeArrayBuffer$1 = global_1[ARRAY_BUFFER$1];
1952 // `ArrayBuffer` constructor
1953 // https://tc39.github.io/ecma262/#sec-arraybuffer-constructor
1954 _export({ global: true, forced: NativeArrayBuffer$1 !== ArrayBuffer$1 }, {
1955 ArrayBuffer: ArrayBuffer$1
1958 setSpecies(ARRAY_BUFFER$1);
1960 var TO_STRING_TAG$1 = wellKnownSymbol('toStringTag');
1963 test[TO_STRING_TAG$1] = 'z';
1965 var toStringTagSupport = String(test) === '[object z]';
1967 var TO_STRING_TAG$2 = wellKnownSymbol('toStringTag');
1969 var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments';
1971 // fallback for IE11 Script Access Denied error
1972 var tryGet = function (it, key) {
1975 } catch (error) { /* empty */ }
1978 // getting tag from ES6+ `Object.prototype.toString`
1979 var classof = toStringTagSupport ? classofRaw : function (it) {
1981 return it === undefined ? 'Undefined' : it === null ? 'Null'
1982 // @@toStringTag case
1983 : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$2)) == 'string' ? tag
1985 : CORRECT_ARGUMENTS ? classofRaw(O)
1986 // ES3 arguments fallback
1987 : (result = classofRaw(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : result;
1990 var defineProperty$5 = objectDefineProperty.f;
1996 var Int8Array$1 = global_1.Int8Array;
1997 var Int8ArrayPrototype = Int8Array$1 && Int8Array$1.prototype;
1998 var Uint8ClampedArray = global_1.Uint8ClampedArray;
1999 var Uint8ClampedArrayPrototype = Uint8ClampedArray && Uint8ClampedArray.prototype;
2000 var TypedArray = Int8Array$1 && objectGetPrototypeOf(Int8Array$1);
2001 var TypedArrayPrototype = Int8ArrayPrototype && objectGetPrototypeOf(Int8ArrayPrototype);
2002 var ObjectPrototype$3 = Object.prototype;
2003 var isPrototypeOf = ObjectPrototype$3.isPrototypeOf;
2005 var TO_STRING_TAG$3 = wellKnownSymbol('toStringTag');
2006 var TYPED_ARRAY_TAG = uid('TYPED_ARRAY_TAG');
2007 // Fixing native typed arrays in Opera Presto crashes the browser, see #595
2008 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferNative && !!objectSetPrototypeOf && classof(global_1.opera) !== 'Opera';
2009 var TYPED_ARRAY_TAG_REQIRED = false;
2012 var TypedArrayConstructorsList = {
2015 Uint8ClampedArray: 1,
2024 var isView = function isView(it) {
2025 var klass = classof(it);
2026 return klass === 'DataView' || has(TypedArrayConstructorsList, klass);
2029 var isTypedArray = function (it) {
2030 return isObject(it) && has(TypedArrayConstructorsList, classof(it));
2033 var aTypedArray = function (it) {
2034 if (isTypedArray(it)) return it;
2035 throw TypeError('Target is not a typed array');
2038 var aTypedArrayConstructor = function (C) {
2039 if (objectSetPrototypeOf) {
2040 if (isPrototypeOf.call(TypedArray, C)) return C;
2041 } else for (var ARRAY in TypedArrayConstructorsList) if (has(TypedArrayConstructorsList, NAME)) {
2042 var TypedArrayConstructor = global_1[ARRAY];
2043 if (TypedArrayConstructor && (C === TypedArrayConstructor || isPrototypeOf.call(TypedArrayConstructor, C))) {
2046 } throw TypeError('Target is not a typed array constructor');
2049 var exportTypedArrayMethod = function (KEY, property, forced) {
2050 if (!descriptors) return;
2051 if (forced) for (var ARRAY in TypedArrayConstructorsList) {
2052 var TypedArrayConstructor = global_1[ARRAY];
2053 if (TypedArrayConstructor && has(TypedArrayConstructor.prototype, KEY)) {
2054 delete TypedArrayConstructor.prototype[KEY];
2057 if (!TypedArrayPrototype[KEY] || forced) {
2058 redefine(TypedArrayPrototype, KEY, forced ? property
2059 : NATIVE_ARRAY_BUFFER_VIEWS && Int8ArrayPrototype[KEY] || property);
2063 var exportTypedArrayStaticMethod = function (KEY, property, forced) {
2064 var ARRAY, TypedArrayConstructor;
2065 if (!descriptors) return;
2066 if (objectSetPrototypeOf) {
2067 if (forced) for (ARRAY in TypedArrayConstructorsList) {
2068 TypedArrayConstructor = global_1[ARRAY];
2069 if (TypedArrayConstructor && has(TypedArrayConstructor, KEY)) {
2070 delete TypedArrayConstructor[KEY];
2073 if (!TypedArray[KEY] || forced) {
2074 // V8 ~ Chrome 49-50 `%TypedArray%` methods are non-writable non-configurable
2076 return redefine(TypedArray, KEY, forced ? property : NATIVE_ARRAY_BUFFER_VIEWS && Int8Array$1[KEY] || property);
2077 } catch (error) { /* empty */ }
2080 for (ARRAY in TypedArrayConstructorsList) {
2081 TypedArrayConstructor = global_1[ARRAY];
2082 if (TypedArrayConstructor && (!TypedArrayConstructor[KEY] || forced)) {
2083 redefine(TypedArrayConstructor, KEY, property);
2088 for (NAME in TypedArrayConstructorsList) {
2089 if (!global_1[NAME]) NATIVE_ARRAY_BUFFER_VIEWS = false;
2092 // WebKit bug - typed arrays constructors prototype is Object.prototype
2093 if (!NATIVE_ARRAY_BUFFER_VIEWS || typeof TypedArray != 'function' || TypedArray === Function.prototype) {
2094 // eslint-disable-next-line no-shadow
2095 TypedArray = function TypedArray() {
2096 throw TypeError('Incorrect invocation');
2098 if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) {
2099 if (global_1[NAME]) objectSetPrototypeOf(global_1[NAME], TypedArray);
2103 if (!NATIVE_ARRAY_BUFFER_VIEWS || !TypedArrayPrototype || TypedArrayPrototype === ObjectPrototype$3) {
2104 TypedArrayPrototype = TypedArray.prototype;
2105 if (NATIVE_ARRAY_BUFFER_VIEWS) for (NAME in TypedArrayConstructorsList) {
2106 if (global_1[NAME]) objectSetPrototypeOf(global_1[NAME].prototype, TypedArrayPrototype);
2110 // WebKit bug - one more object in Uint8ClampedArray prototype chain
2111 if (NATIVE_ARRAY_BUFFER_VIEWS && objectGetPrototypeOf(Uint8ClampedArrayPrototype) !== TypedArrayPrototype) {
2112 objectSetPrototypeOf(Uint8ClampedArrayPrototype, TypedArrayPrototype);
2115 if (descriptors && !has(TypedArrayPrototype, TO_STRING_TAG$3)) {
2116 TYPED_ARRAY_TAG_REQIRED = true;
2117 defineProperty$5(TypedArrayPrototype, TO_STRING_TAG$3, { get: function () {
2118 return isObject(this) ? this[TYPED_ARRAY_TAG] : undefined;
2120 for (NAME in TypedArrayConstructorsList) if (global_1[NAME]) {
2121 createNonEnumerableProperty(global_1[NAME], TYPED_ARRAY_TAG, NAME);
2125 var arrayBufferViewCore = {
2126 NATIVE_ARRAY_BUFFER_VIEWS: NATIVE_ARRAY_BUFFER_VIEWS,
2127 TYPED_ARRAY_TAG: TYPED_ARRAY_TAG_REQIRED && TYPED_ARRAY_TAG,
2128 aTypedArray: aTypedArray,
2129 aTypedArrayConstructor: aTypedArrayConstructor,
2130 exportTypedArrayMethod: exportTypedArrayMethod,
2131 exportTypedArrayStaticMethod: exportTypedArrayStaticMethod,
2133 isTypedArray: isTypedArray,
2134 TypedArray: TypedArray,
2135 TypedArrayPrototype: TypedArrayPrototype
2138 var NATIVE_ARRAY_BUFFER_VIEWS$1 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
2140 // `ArrayBuffer.isView` method
2141 // https://tc39.github.io/ecma262/#sec-arraybuffer.isview
2142 _export({ target: 'ArrayBuffer', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS$1 }, {
2143 isView: arrayBufferViewCore.isView
2146 var SPECIES$4 = wellKnownSymbol('species');
2148 // `SpeciesConstructor` abstract operation
2149 // https://tc39.github.io/ecma262/#sec-speciesconstructor
2150 var speciesConstructor = function (O, defaultConstructor) {
2151 var C = anObject(O).constructor;
2153 return C === undefined || (S = anObject(C)[SPECIES$4]) == undefined ? defaultConstructor : aFunction$1(S);
2156 var ArrayBuffer$2 = arrayBuffer.ArrayBuffer;
2157 var DataView$1 = arrayBuffer.DataView;
2158 var nativeArrayBufferSlice = ArrayBuffer$2.prototype.slice;
2160 var INCORRECT_SLICE = fails(function () {
2161 return !new ArrayBuffer$2(2).slice(1, undefined).byteLength;
2164 // `ArrayBuffer.prototype.slice` method
2165 // https://tc39.github.io/ecma262/#sec-arraybuffer.prototype.slice
2166 _export({ target: 'ArrayBuffer', proto: true, unsafe: true, forced: INCORRECT_SLICE }, {
2167 slice: function slice(start, end) {
2168 if (nativeArrayBufferSlice !== undefined && end === undefined) {
2169 return nativeArrayBufferSlice.call(anObject(this), start); // FF fix
2171 var length = anObject(this).byteLength;
2172 var first = toAbsoluteIndex(start, length);
2173 var fin = toAbsoluteIndex(end === undefined ? length : end, length);
2174 var result = new (speciesConstructor(this, ArrayBuffer$2))(toLength(fin - first));
2175 var viewSource = new DataView$1(this);
2176 var viewTarget = new DataView$1(result);
2178 while (first < fin) {
2179 viewTarget.setUint8(index++, viewSource.getUint8(first++));
2184 // `DataView` constructor
2185 // https://tc39.github.io/ecma262/#sec-dataview-constructor
2186 _export({ global: true, forced: !arrayBufferNative }, {
2187 DataView: arrayBuffer.DataView
2190 var defineProperty$6 = objectDefineProperty.f;
2192 var FunctionPrototype = Function.prototype;
2193 var FunctionPrototypeToString = FunctionPrototype.toString;
2194 var nameRE = /^\s*function ([^ (]*)/;
2195 var NAME$1 = 'name';
2197 // Function instances `.name` property
2198 // https://tc39.github.io/ecma262/#sec-function-instances-name
2199 if (descriptors && !(NAME$1 in FunctionPrototype)) {
2200 defineProperty$6(FunctionPrototype, NAME$1, {
2204 return FunctionPrototypeToString.call(this).match(nameRE)[1];
2212 // `Object.create` method
2213 // https://tc39.github.io/ecma262/#sec-object.create
2214 _export({ target: 'Object', stat: true, sham: !descriptors }, {
2215 create: objectCreate
2218 var nativeGetOwnPropertyNames$2 = objectGetOwnPropertyNamesExternal.f;
2220 var FAILS_ON_PRIMITIVES = fails(function () { return !Object.getOwnPropertyNames(1); });
2222 // `Object.getOwnPropertyNames` method
2223 // https://tc39.github.io/ecma262/#sec-object.getownpropertynames
2224 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES }, {
2225 getOwnPropertyNames: nativeGetOwnPropertyNames$2
2228 // `Object.prototype.toString` method implementation
2229 // https://tc39.github.io/ecma262/#sec-object.prototype.tostring
2230 var objectToString = toStringTagSupport ? {}.toString : function toString() {
2231 return '[object ' + classof(this) + ']';
2234 // `Object.prototype.toString` method
2235 // https://tc39.github.io/ecma262/#sec-object.prototype.tostring
2236 if (!toStringTagSupport) {
2237 redefine(Object.prototype, 'toString', objectToString, { unsafe: true });
2240 var nativePromiseConstructor = global_1.Promise;
2242 var ITERATOR$2 = wellKnownSymbol('iterator');
2243 var ArrayPrototype$1 = Array.prototype;
2245 // check on default Array iterator
2246 var isArrayIteratorMethod = function (it) {
2247 return it !== undefined && (iterators.Array === it || ArrayPrototype$1[ITERATOR$2] === it);
2250 var ITERATOR$3 = wellKnownSymbol('iterator');
2252 var getIteratorMethod = function (it) {
2253 if (it != undefined) return it[ITERATOR$3]
2255 || iterators[classof(it)];
2258 // call something on iterator step with safe closing on error
2259 var callWithSafeIterationClosing = function (iterator, fn, value, ENTRIES) {
2261 return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);
2262 // 7.4.6 IteratorClose(iterator, completion)
2264 var returnMethod = iterator['return'];
2265 if (returnMethod !== undefined) anObject(returnMethod.call(iterator));
2270 var iterate_1 = createCommonjsModule(function (module) {
2271 var Result = function (stopped, result) {
2272 this.stopped = stopped;
2273 this.result = result;
2276 var iterate = module.exports = function (iterable, fn, that, AS_ENTRIES, IS_ITERATOR) {
2277 var boundFunction = functionBindContext(fn, that, AS_ENTRIES ? 2 : 1);
2278 var iterator, iterFn, index, length, result, next, step;
2281 iterator = iterable;
2283 iterFn = getIteratorMethod(iterable);
2284 if (typeof iterFn != 'function') throw TypeError('Target is not iterable');
2285 // optimisation for array iterators
2286 if (isArrayIteratorMethod(iterFn)) {
2287 for (index = 0, length = toLength(iterable.length); length > index; index++) {
2289 ? boundFunction(anObject(step = iterable[index])[0], step[1])
2290 : boundFunction(iterable[index]);
2291 if (result && result instanceof Result) return result;
2292 } return new Result(false);
2294 iterator = iterFn.call(iterable);
2297 next = iterator.next;
2298 while (!(step = next.call(iterator)).done) {
2299 result = callWithSafeIterationClosing(iterator, boundFunction, step.value, AS_ENTRIES);
2300 if (typeof result == 'object' && result && result instanceof Result) return result;
2301 } return new Result(false);
2304 iterate.stop = function (result) {
2305 return new Result(true, result);
2309 var ITERATOR$4 = wellKnownSymbol('iterator');
2310 var SAFE_CLOSING = false;
2314 var iteratorWithReturn = {
2316 return { done: !!called++ };
2318 'return': function () {
2319 SAFE_CLOSING = true;
2322 iteratorWithReturn[ITERATOR$4] = function () {
2325 // eslint-disable-next-line no-throw-literal
2326 Array.from(iteratorWithReturn, function () { throw 2; });
2327 } catch (error) { /* empty */ }
2329 var checkCorrectnessOfIteration = function (exec, SKIP_CLOSING) {
2330 if (!SKIP_CLOSING && !SAFE_CLOSING) return false;
2331 var ITERATION_SUPPORT = false;
2334 object[ITERATOR$4] = function () {
2337 return { done: ITERATION_SUPPORT = true };
2342 } catch (error) { /* empty */ }
2343 return ITERATION_SUPPORT;
2346 var engineIsIos = /(iphone|ipod|ipad).*applewebkit/i.test(engineUserAgent);
2348 var location$1 = global_1.location;
2349 var set$2 = global_1.setImmediate;
2350 var clear = global_1.clearImmediate;
2351 var process$2 = global_1.process;
2352 var MessageChannel = global_1.MessageChannel;
2353 var Dispatch = global_1.Dispatch;
2356 var ONREADYSTATECHANGE = 'onreadystatechange';
2357 var defer, channel, port;
2359 var run = function (id) {
2360 // eslint-disable-next-line no-prototype-builtins
2361 if (queue.hasOwnProperty(id)) {
2368 var runner = function (id) {
2369 return function () {
2374 var listener = function (event) {
2378 var post = function (id) {
2379 // old engines have not location.origin
2380 global_1.postMessage(id + '', location$1.protocol + '//' + location$1.host);
2383 // Node.js 0.9+ & IE10+ has setImmediate, otherwise:
2384 if (!set$2 || !clear) {
2385 set$2 = function setImmediate(fn) {
2388 while (arguments.length > i) args.push(arguments[i++]);
2389 queue[++counter] = function () {
2390 // eslint-disable-next-line no-new-func
2391 (typeof fn == 'function' ? fn : Function(fn)).apply(undefined, args);
2396 clear = function clearImmediate(id) {
2400 if (classofRaw(process$2) == 'process') {
2401 defer = function (id) {
2402 process$2.nextTick(runner(id));
2404 // Sphere (JS game engine) Dispatch API
2405 } else if (Dispatch && Dispatch.now) {
2406 defer = function (id) {
2407 Dispatch.now(runner(id));
2409 // Browsers with MessageChannel, includes WebWorkers
2410 // except iOS - https://github.com/zloirock/core-js/issues/624
2411 } else if (MessageChannel && !engineIsIos) {
2412 channel = new MessageChannel();
2413 port = channel.port2;
2414 channel.port1.onmessage = listener;
2415 defer = functionBindContext(port.postMessage, port, 1);
2416 // Browsers with postMessage, skip WebWorkers
2417 // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
2419 global_1.addEventListener &&
2420 typeof postMessage == 'function' &&
2421 !global_1.importScripts &&
2423 location$1.protocol !== 'file:'
2426 global_1.addEventListener('message', listener, false);
2428 } else if (ONREADYSTATECHANGE in documentCreateElement('script')) {
2429 defer = function (id) {
2430 html.appendChild(documentCreateElement('script'))[ONREADYSTATECHANGE] = function () {
2431 html.removeChild(this);
2435 // Rest old browsers
2437 defer = function (id) {
2438 setTimeout(runner(id), 0);
2448 var getOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
2450 var macrotask = task.set;
2453 var MutationObserver = global_1.MutationObserver || global_1.WebKitMutationObserver;
2454 var process$3 = global_1.process;
2455 var Promise$1 = global_1.Promise;
2456 var IS_NODE = classofRaw(process$3) == 'process';
2457 // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask`
2458 var queueMicrotaskDescriptor = getOwnPropertyDescriptor$2(global_1, 'queueMicrotask');
2459 var queueMicrotask = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value;
2461 var flush, head, last, notify, toggle, node, promise, then;
2463 // modern engines have queueMicrotask method
2464 if (!queueMicrotask) {
2465 flush = function () {
2467 if (IS_NODE && (parent = process$3.domain)) parent.exit();
2475 else last = undefined;
2479 if (parent) parent.enter();
2484 notify = function () {
2485 process$3.nextTick(flush);
2487 // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339
2488 } else if (MutationObserver && !engineIsIos) {
2490 node = document.createTextNode('');
2491 new MutationObserver(flush).observe(node, { characterData: true });
2492 notify = function () {
2493 node.data = toggle = !toggle;
2495 // environments with maybe non-completely correct, but existent Promise
2496 } else if (Promise$1 && Promise$1.resolve) {
2497 // Promise.resolve without an argument throws an error in LG WebOS 2
2498 promise = Promise$1.resolve(undefined);
2499 then = promise.then;
2500 notify = function () {
2501 then.call(promise, flush);
2503 // for other environments - macrotask based on:
2506 // - window.postMessag
2507 // - onreadystatechange
2510 notify = function () {
2511 // strange IE + webpack dev server bug - use .call(global)
2512 macrotask.call(global_1, flush);
2517 var microtask = queueMicrotask || function (fn) {
2518 var task = { fn: fn, next: undefined };
2519 if (last) last.next = task;
2526 var PromiseCapability = function (C) {
2527 var resolve, reject;
2528 this.promise = new C(function ($$resolve, $$reject) {
2529 if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor');
2530 resolve = $$resolve;
2533 this.resolve = aFunction$1(resolve);
2534 this.reject = aFunction$1(reject);
2537 // 25.4.1.5 NewPromiseCapability(C)
2538 var f$7 = function (C) {
2539 return new PromiseCapability(C);
2542 var newPromiseCapability = {
2546 var promiseResolve = function (C, x) {
2548 if (isObject(x) && x.constructor === C) return x;
2549 var promiseCapability = newPromiseCapability.f(C);
2550 var resolve = promiseCapability.resolve;
2552 return promiseCapability.promise;
2555 var hostReportErrors = function (a, b) {
2556 var console = global_1.console;
2557 if (console && console.error) {
2558 arguments.length === 1 ? console.error(a) : console.error(a, b);
2562 var perform = function (exec) {
2564 return { error: false, value: exec() };
2566 return { error: true, value: error };
2570 var task$1 = task.set;
2581 var SPECIES$5 = wellKnownSymbol('species');
2582 var PROMISE = 'Promise';
2583 var getInternalState$3 = internalState.get;
2584 var setInternalState$3 = internalState.set;
2585 var getInternalPromiseState = internalState.getterFor(PROMISE);
2586 var PromiseConstructor = nativePromiseConstructor;
2587 var TypeError$1 = global_1.TypeError;
2588 var document$2 = global_1.document;
2589 var process$4 = global_1.process;
2590 var $fetch = getBuiltIn('fetch');
2591 var newPromiseCapability$1 = newPromiseCapability.f;
2592 var newGenericPromiseCapability = newPromiseCapability$1;
2593 var IS_NODE$1 = classofRaw(process$4) == 'process';
2594 var DISPATCH_EVENT = !!(document$2 && document$2.createEvent && global_1.dispatchEvent);
2595 var UNHANDLED_REJECTION = 'unhandledrejection';
2596 var REJECTION_HANDLED = 'rejectionhandled';
2602 var Internal, OwnPromiseCapability, PromiseWrapper, nativeThen;
2604 var FORCED = isForced_1(PROMISE, function () {
2605 var GLOBAL_CORE_JS_PROMISE = inspectSource(PromiseConstructor) !== String(PromiseConstructor);
2606 if (!GLOBAL_CORE_JS_PROMISE) {
2607 // V8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
2608 // https://bugs.chromium.org/p/chromium/issues/detail?id=830565
2609 // We can't detect it synchronously, so just check versions
2610 if (engineV8Version === 66) return true;
2611 // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test
2612 if (!IS_NODE$1 && typeof PromiseRejectionEvent != 'function') return true;
2614 // We can't use @@species feature detection in V8 since it causes
2615 // deoptimization and performance degradation
2616 // https://github.com/zloirock/core-js/issues/679
2617 if (engineV8Version >= 51 && /native code/.test(PromiseConstructor)) return false;
2618 // Detect correctness of subclassing with @@species support
2619 var promise = PromiseConstructor.resolve(1);
2620 var FakePromise = function (exec) {
2621 exec(function () { /* empty */ }, function () { /* empty */ });
2623 var constructor = promise.constructor = {};
2624 constructor[SPECIES$5] = FakePromise;
2625 return !(promise.then(function () { /* empty */ }) instanceof FakePromise);
2628 var INCORRECT_ITERATION = FORCED || !checkCorrectnessOfIteration(function (iterable) {
2629 PromiseConstructor.all(iterable)['catch'](function () { /* empty */ });
2633 var isThenable = function (it) {
2635 return isObject(it) && typeof (then = it.then) == 'function' ? then : false;
2638 var notify$1 = function (promise, state, isReject) {
2639 if (state.notified) return;
2640 state.notified = true;
2641 var chain = state.reactions;
2642 microtask(function () {
2643 var value = state.value;
2644 var ok = state.state == FULFILLED;
2646 // variable length - can't use forEach
2647 while (chain.length > index) {
2648 var reaction = chain[index++];
2649 var handler = ok ? reaction.ok : reaction.fail;
2650 var resolve = reaction.resolve;
2651 var reject = reaction.reject;
2652 var domain = reaction.domain;
2653 var result, then, exited;
2657 if (state.rejection === UNHANDLED) onHandleUnhandled(promise, state);
2658 state.rejection = HANDLED;
2660 if (handler === true) result = value;
2662 if (domain) domain.enter();
2663 result = handler(value); // can throw
2669 if (result === reaction.promise) {
2670 reject(TypeError$1('Promise-chain cycle'));
2671 } else if (then = isThenable(result)) {
2672 then.call(result, resolve, reject);
2673 } else resolve(result);
2674 } else reject(value);
2676 if (domain && !exited) domain.exit();
2680 state.reactions = [];
2681 state.notified = false;
2682 if (isReject && !state.rejection) onUnhandled(promise, state);
2686 var dispatchEvent = function (name, promise, reason) {
2688 if (DISPATCH_EVENT) {
2689 event = document$2.createEvent('Event');
2690 event.promise = promise;
2691 event.reason = reason;
2692 event.initEvent(name, false, true);
2693 global_1.dispatchEvent(event);
2694 } else event = { promise: promise, reason: reason };
2695 if (handler = global_1['on' + name]) handler(event);
2696 else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason);
2699 var onUnhandled = function (promise, state) {
2700 task$1.call(global_1, function () {
2701 var value = state.value;
2702 var IS_UNHANDLED = isUnhandled(state);
2705 result = perform(function () {
2707 process$4.emit('unhandledRejection', value, promise);
2708 } else dispatchEvent(UNHANDLED_REJECTION, promise, value);
2710 // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
2711 state.rejection = IS_NODE$1 || isUnhandled(state) ? UNHANDLED : HANDLED;
2712 if (result.error) throw result.value;
2717 var isUnhandled = function (state) {
2718 return state.rejection !== HANDLED && !state.parent;
2721 var onHandleUnhandled = function (promise, state) {
2722 task$1.call(global_1, function () {
2724 process$4.emit('rejectionHandled', promise);
2725 } else dispatchEvent(REJECTION_HANDLED, promise, state.value);
2729 var bind = function (fn, promise, state, unwrap) {
2730 return function (value) {
2731 fn(promise, state, value, unwrap);
2735 var internalReject = function (promise, state, value, unwrap) {
2736 if (state.done) return;
2738 if (unwrap) state = unwrap;
2739 state.value = value;
2740 state.state = REJECTED;
2741 notify$1(promise, state, true);
2744 var internalResolve = function (promise, state, value, unwrap) {
2745 if (state.done) return;
2747 if (unwrap) state = unwrap;
2749 if (promise === value) throw TypeError$1("Promise can't be resolved itself");
2750 var then = isThenable(value);
2752 microtask(function () {
2753 var wrapper = { done: false };
2756 bind(internalResolve, promise, wrapper, state),
2757 bind(internalReject, promise, wrapper, state)
2760 internalReject(promise, wrapper, error, state);
2764 state.value = value;
2765 state.state = FULFILLED;
2766 notify$1(promise, state, false);
2769 internalReject(promise, { done: false }, error, state);
2773 // constructor polyfill
2775 // 25.4.3.1 Promise(executor)
2776 PromiseConstructor = function Promise(executor) {
2777 anInstance(this, PromiseConstructor, PROMISE);
2778 aFunction$1(executor);
2779 Internal.call(this);
2780 var state = getInternalState$3(this);
2782 executor(bind(internalResolve, this, state), bind(internalReject, this, state));
2784 internalReject(this, state, error);
2787 // eslint-disable-next-line no-unused-vars
2788 Internal = function Promise(executor) {
2789 setInternalState$3(this, {
2800 Internal.prototype = redefineAll(PromiseConstructor.prototype, {
2801 // `Promise.prototype.then` method
2802 // https://tc39.github.io/ecma262/#sec-promise.prototype.then
2803 then: function then(onFulfilled, onRejected) {
2804 var state = getInternalPromiseState(this);
2805 var reaction = newPromiseCapability$1(speciesConstructor(this, PromiseConstructor));
2806 reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
2807 reaction.fail = typeof onRejected == 'function' && onRejected;
2808 reaction.domain = IS_NODE$1 ? process$4.domain : undefined;
2809 state.parent = true;
2810 state.reactions.push(reaction);
2811 if (state.state != PENDING) notify$1(this, state, false);
2812 return reaction.promise;
2814 // `Promise.prototype.catch` method
2815 // https://tc39.github.io/ecma262/#sec-promise.prototype.catch
2816 'catch': function (onRejected) {
2817 return this.then(undefined, onRejected);
2820 OwnPromiseCapability = function () {
2821 var promise = new Internal();
2822 var state = getInternalState$3(promise);
2823 this.promise = promise;
2824 this.resolve = bind(internalResolve, promise, state);
2825 this.reject = bind(internalReject, promise, state);
2827 newPromiseCapability.f = newPromiseCapability$1 = function (C) {
2828 return C === PromiseConstructor || C === PromiseWrapper
2829 ? new OwnPromiseCapability(C)
2830 : newGenericPromiseCapability(C);
2833 if ( typeof nativePromiseConstructor == 'function') {
2834 nativeThen = nativePromiseConstructor.prototype.then;
2836 // wrap native Promise#then for native async functions
2837 redefine(nativePromiseConstructor.prototype, 'then', function then(onFulfilled, onRejected) {
2839 return new PromiseConstructor(function (resolve, reject) {
2840 nativeThen.call(that, resolve, reject);
2841 }).then(onFulfilled, onRejected);
2842 // https://github.com/zloirock/core-js/issues/640
2843 }, { unsafe: true });
2845 // wrap fetch result
2846 if (typeof $fetch == 'function') _export({ global: true, enumerable: true, forced: true }, {
2847 // eslint-disable-next-line no-unused-vars
2848 fetch: function fetch(input /* , init */) {
2849 return promiseResolve(PromiseConstructor, $fetch.apply(global_1, arguments));
2855 _export({ global: true, wrap: true, forced: FORCED }, {
2856 Promise: PromiseConstructor
2859 setToStringTag(PromiseConstructor, PROMISE, false);
2860 setSpecies(PROMISE);
2862 PromiseWrapper = getBuiltIn(PROMISE);
2865 _export({ target: PROMISE, stat: true, forced: FORCED }, {
2866 // `Promise.reject` method
2867 // https://tc39.github.io/ecma262/#sec-promise.reject
2868 reject: function reject(r) {
2869 var capability = newPromiseCapability$1(this);
2870 capability.reject.call(undefined, r);
2871 return capability.promise;
2875 _export({ target: PROMISE, stat: true, forced: FORCED }, {
2876 // `Promise.resolve` method
2877 // https://tc39.github.io/ecma262/#sec-promise.resolve
2878 resolve: function resolve(x) {
2879 return promiseResolve( this, x);
2883 _export({ target: PROMISE, stat: true, forced: INCORRECT_ITERATION }, {
2884 // `Promise.all` method
2885 // https://tc39.github.io/ecma262/#sec-promise.all
2886 all: function all(iterable) {
2888 var capability = newPromiseCapability$1(C);
2889 var resolve = capability.resolve;
2890 var reject = capability.reject;
2891 var result = perform(function () {
2892 var $promiseResolve = aFunction$1(C.resolve);
2896 iterate_1(iterable, function (promise) {
2897 var index = counter++;
2898 var alreadyCalled = false;
2899 values.push(undefined);
2901 $promiseResolve.call(C, promise).then(function (value) {
2902 if (alreadyCalled) return;
2903 alreadyCalled = true;
2904 values[index] = value;
2905 --remaining || resolve(values);
2908 --remaining || resolve(values);
2910 if (result.error) reject(result.value);
2911 return capability.promise;
2913 // `Promise.race` method
2914 // https://tc39.github.io/ecma262/#sec-promise.race
2915 race: function race(iterable) {
2917 var capability = newPromiseCapability$1(C);
2918 var reject = capability.reject;
2919 var result = perform(function () {
2920 var $promiseResolve = aFunction$1(C.resolve);
2921 iterate_1(iterable, function (promise) {
2922 $promiseResolve.call(C, promise).then(capability.resolve, reject);
2925 if (result.error) reject(result.value);
2926 return capability.promise;
2930 // `RegExp.prototype.flags` getter implementation
2931 // https://tc39.github.io/ecma262/#sec-get-regexp.prototype.flags
2932 var regexpFlags = function () {
2933 var that = anObject(this);
2935 if (that.global) result += 'g';
2936 if (that.ignoreCase) result += 'i';
2937 if (that.multiline) result += 'm';
2938 if (that.dotAll) result += 's';
2939 if (that.unicode) result += 'u';
2940 if (that.sticky) result += 'y';
2944 // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError,
2945 // so we use an intermediate function.
2947 return RegExp(s, f);
2950 var UNSUPPORTED_Y = fails(function () {
2951 // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError
2952 var re = RE('a', 'y');
2954 return re.exec('abcd') != null;
2957 var BROKEN_CARET = fails(function () {
2958 // https://bugzilla.mozilla.org/show_bug.cgi?id=773687
2959 var re = RE('^r', 'gy');
2961 return re.exec('str') != null;
2964 var regexpStickyHelpers = {
2965 UNSUPPORTED_Y: UNSUPPORTED_Y,
2966 BROKEN_CARET: BROKEN_CARET
2969 var nativeExec = RegExp.prototype.exec;
2970 // This always refers to the native implementation, because the
2971 // String#replace polyfill uses ./fix-regexp-well-known-symbol-logic.js,
2972 // which loads this file before patching the method.
2973 var nativeReplace = String.prototype.replace;
2975 var patchedExec = nativeExec;
2977 var UPDATES_LAST_INDEX_WRONG = (function () {
2980 nativeExec.call(re1, 'a');
2981 nativeExec.call(re2, 'a');
2982 return re1.lastIndex !== 0 || re2.lastIndex !== 0;
2985 var UNSUPPORTED_Y$1 = regexpStickyHelpers.UNSUPPORTED_Y || regexpStickyHelpers.BROKEN_CARET;
2987 // nonparticipating capturing group, copied from es5-shim's String#split patch.
2988 var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;
2990 var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y$1;
2993 patchedExec = function exec(str) {
2995 var lastIndex, reCopy, match, i;
2996 var sticky = UNSUPPORTED_Y$1 && re.sticky;
2997 var flags = regexpFlags.call(re);
2998 var source = re.source;
3003 flags = flags.replace('y', '');
3004 if (flags.indexOf('g') === -1) {
3008 strCopy = String(str).slice(re.lastIndex);
3009 // Support anchored sticky behavior.
3010 if (re.lastIndex > 0 && (!re.multiline || re.multiline && str[re.lastIndex - 1] !== '\n')) {
3011 source = '(?: ' + source + ')';
3012 strCopy = ' ' + strCopy;
3015 // ^(? + rx + ) is needed, in combination with some str slicing, to
3016 // simulate the 'y' flag.
3017 reCopy = new RegExp('^(?:' + source + ')', flags);
3020 if (NPCG_INCLUDED) {
3021 reCopy = new RegExp('^' + source + '$(?!\\s)', flags);
3023 if (UPDATES_LAST_INDEX_WRONG) lastIndex = re.lastIndex;
3025 match = nativeExec.call(sticky ? reCopy : re, strCopy);
3029 match.input = match.input.slice(charsAdded);
3030 match[0] = match[0].slice(charsAdded);
3031 match.index = re.lastIndex;
3032 re.lastIndex += match[0].length;
3033 } else re.lastIndex = 0;
3034 } else if (UPDATES_LAST_INDEX_WRONG && match) {
3035 re.lastIndex = re.global ? match.index + match[0].length : lastIndex;
3037 if (NPCG_INCLUDED && match && match.length > 1) {
3038 // Fix browsers whose `exec` methods don't consistently return `undefined`
3039 // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/
3040 nativeReplace.call(match[0], reCopy, function () {
3041 for (i = 1; i < arguments.length - 2; i++) {
3042 if (arguments[i] === undefined) match[i] = undefined;
3051 var regexpExec = patchedExec;
3053 _export({ target: 'RegExp', proto: true, forced: /./.exec !== regexpExec }, {
3057 var TO_STRING$1 = 'toString';
3058 var RegExpPrototype = RegExp.prototype;
3059 var nativeToString = RegExpPrototype[TO_STRING$1];
3061 var NOT_GENERIC = fails(function () { return nativeToString.call({ source: 'a', flags: 'b' }) != '/a/b'; });
3062 // FF44- RegExp#toString has a wrong name
3063 var INCORRECT_NAME = nativeToString.name != TO_STRING$1;
3065 // `RegExp.prototype.toString` method
3066 // https://tc39.github.io/ecma262/#sec-regexp.prototype.tostring
3067 if (NOT_GENERIC || INCORRECT_NAME) {
3068 redefine(RegExp.prototype, TO_STRING$1, function toString() {
3069 var R = anObject(this);
3070 var p = String(R.source);
3072 var f = String(rf === undefined && R instanceof RegExp && !('flags' in RegExpPrototype) ? regexpFlags.call(R) : rf);
3073 return '/' + p + '/' + f;
3074 }, { unsafe: true });
3077 // `String.prototype.{ codePointAt, at }` methods implementation
3078 var createMethod$2 = function (CONVERT_TO_STRING) {
3079 return function ($this, pos) {
3080 var S = String(requireObjectCoercible($this));
3081 var position = toInteger(pos);
3082 var size = S.length;
3084 if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined;
3085 first = S.charCodeAt(position);
3086 return first < 0xD800 || first > 0xDBFF || position + 1 === size
3087 || (second = S.charCodeAt(position + 1)) < 0xDC00 || second > 0xDFFF
3088 ? CONVERT_TO_STRING ? S.charAt(position) : first
3089 : CONVERT_TO_STRING ? S.slice(position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000;
3093 var stringMultibyte = {
3094 // `String.prototype.codePointAt` method
3095 // https://tc39.github.io/ecma262/#sec-string.prototype.codepointat
3096 codeAt: createMethod$2(false),
3097 // `String.prototype.at` method
3098 // https://github.com/mathiasbynens/String.prototype.at
3099 charAt: createMethod$2(true)
3102 var charAt = stringMultibyte.charAt;
3106 var STRING_ITERATOR = 'String Iterator';
3107 var setInternalState$4 = internalState.set;
3108 var getInternalState$4 = internalState.getterFor(STRING_ITERATOR);
3110 // `String.prototype[@@iterator]` method
3111 // https://tc39.github.io/ecma262/#sec-string.prototype-@@iterator
3112 defineIterator(String, 'String', function (iterated) {
3113 setInternalState$4(this, {
3114 type: STRING_ITERATOR,
3115 string: String(iterated),
3118 // `%StringIteratorPrototype%.next` method
3119 // https://tc39.github.io/ecma262/#sec-%stringiteratorprototype%.next
3120 }, function next() {
3121 var state = getInternalState$4(this);
3122 var string = state.string;
3123 var index = state.index;
3125 if (index >= string.length) return { value: undefined, done: true };
3126 point = charAt(string, index);
3127 state.index += point.length;
3128 return { value: point, done: false };
3131 // TODO: Remove from `core-js@4` since it's moved to entry points
3139 var SPECIES$6 = wellKnownSymbol('species');
3141 var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () {
3142 // #replace needs built-in support for named groups.
3143 // #match works fine because it just return the exec results, even if it has
3144 // a "grops" property.
3146 re.exec = function () {
3148 result.groups = { a: '7' };
3151 return ''.replace(re, '$<a>') !== '7';
3154 // IE <= 11 replaces $0 with the whole match, as if it was $&
3155 // https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0
3156 var REPLACE_KEEPS_$0 = (function () {
3157 return 'a'.replace(/./, '$0') === '$0';
3160 var REPLACE = wellKnownSymbol('replace');
3161 // Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string
3162 var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () {
3164 return /./[REPLACE]('a', '$0') === '';
3169 // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec
3170 // Weex JS has frozen built-in prototypes, so use try / catch wrapper
3171 var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () {
3173 var originalExec = re.exec;
3174 re.exec = function () { return originalExec.apply(this, arguments); };
3175 var result = 'ab'.split(re);
3176 return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b';
3179 var fixRegexpWellKnownSymbolLogic = function (KEY, length, exec, sham) {
3180 var SYMBOL = wellKnownSymbol(KEY);
3182 var DELEGATES_TO_SYMBOL = !fails(function () {
3183 // String methods call symbol-named RegEp methods
3185 O[SYMBOL] = function () { return 7; };
3186 return ''[KEY](O) != 7;
3189 var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL && !fails(function () {
3190 // Symbol-named RegExp methods call .exec
3191 var execCalled = false;
3194 if (KEY === 'split') {
3195 // We can't use real regex here since it causes deoptimization
3196 // and serious performance degradation in V8
3197 // https://github.com/zloirock/core-js/issues/306
3199 // RegExp[@@split] doesn't call the regex's exec method, but first creates
3200 // a new one. We need to return the patched regex when creating the new one.
3201 re.constructor = {};
3202 re.constructor[SPECIES$6] = function () { return re; };
3204 re[SYMBOL] = /./[SYMBOL];
3207 re.exec = function () { execCalled = true; return null; };
3214 !DELEGATES_TO_SYMBOL ||
3215 !DELEGATES_TO_EXEC ||
3216 (KEY === 'replace' && !(
3217 REPLACE_SUPPORTS_NAMED_GROUPS &&
3219 !REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE
3221 (KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC)
3223 var nativeRegExpMethod = /./[SYMBOL];
3224 var methods = exec(SYMBOL, ''[KEY], function (nativeMethod, regexp, str, arg2, forceStringMethod) {
3225 if (regexp.exec === regexpExec) {
3226 if (DELEGATES_TO_SYMBOL && !forceStringMethod) {
3227 // The native String method already delegates to @@method (this
3228 // polyfilled function), leasing to infinite recursion.
3229 // We avoid it by directly calling the native @@method method.
3230 return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) };
3232 return { done: true, value: nativeMethod.call(str, regexp, arg2) };
3234 return { done: false };
3236 REPLACE_KEEPS_$0: REPLACE_KEEPS_$0,
3237 REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE: REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE
3239 var stringMethod = methods[0];
3240 var regexMethod = methods[1];
3242 redefine(String.prototype, KEY, stringMethod);
3243 redefine(RegExp.prototype, SYMBOL, length == 2
3244 // 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue)
3245 // 21.2.5.11 RegExp.prototype[@@split](string, limit)
3246 ? function (string, arg) { return regexMethod.call(string, this, arg); }
3247 // 21.2.5.6 RegExp.prototype[@@match](string)
3248 // 21.2.5.9 RegExp.prototype[@@search](string)
3249 : function (string) { return regexMethod.call(string, this); }
3253 if (sham) createNonEnumerableProperty(RegExp.prototype[SYMBOL], 'sham', true);
3256 var charAt$1 = stringMultibyte.charAt;
3258 // `AdvanceStringIndex` abstract operation
3259 // https://tc39.github.io/ecma262/#sec-advancestringindex
3260 var advanceStringIndex = function (S, index, unicode) {
3261 return index + (unicode ? charAt$1(S, index).length : 1);
3264 // `RegExpExec` abstract operation
3265 // https://tc39.github.io/ecma262/#sec-regexpexec
3266 var regexpExecAbstract = function (R, S) {
3268 if (typeof exec === 'function') {
3269 var result = exec.call(R, S);
3270 if (typeof result !== 'object') {
3271 throw TypeError('RegExp exec method returned something other than an Object or null');
3276 if (classofRaw(R) !== 'RegExp') {
3277 throw TypeError('RegExp#exec called on incompatible receiver');
3280 return regexpExec.call(R, S);
3283 var max$2 = Math.max;
3284 var min$2 = Math.min;
3285 var floor$2 = Math.floor;
3286 var SUBSTITUTION_SYMBOLS = /\$([$&'`]|\d\d?|<[^>]*>)/g;
3287 var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&'`]|\d\d?)/g;
3289 var maybeToString = function (it) {
3290 return it === undefined ? it : String(it);
3294 fixRegexpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, maybeCallNative, reason) {
3295 var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = reason.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE;
3296 var REPLACE_KEEPS_$0 = reason.REPLACE_KEEPS_$0;
3297 var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
3300 // `String.prototype.replace` method
3301 // https://tc39.github.io/ecma262/#sec-string.prototype.replace
3302 function replace(searchValue, replaceValue) {
3303 var O = requireObjectCoercible(this);
3304 var replacer = searchValue == undefined ? undefined : searchValue[REPLACE];
3305 return replacer !== undefined
3306 ? replacer.call(searchValue, O, replaceValue)
3307 : nativeReplace.call(String(O), searchValue, replaceValue);
3309 // `RegExp.prototype[@@replace]` method
3310 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace
3311 function (regexp, replaceValue) {
3313 (!REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE && REPLACE_KEEPS_$0) ||
3314 (typeof replaceValue === 'string' && replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1)
3316 var res = maybeCallNative(nativeReplace, regexp, this, replaceValue);
3317 if (res.done) return res.value;
3320 var rx = anObject(regexp);
3321 var S = String(this);
3323 var functionalReplace = typeof replaceValue === 'function';
3324 if (!functionalReplace) replaceValue = String(replaceValue);
3326 var global = rx.global;
3328 var fullUnicode = rx.unicode;
3333 var result = regexpExecAbstract(rx, S);
3334 if (result === null) break;
3336 results.push(result);
3339 var matchStr = String(result[0]);
3340 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
3343 var accumulatedResult = '';
3344 var nextSourcePosition = 0;
3345 for (var i = 0; i < results.length; i++) {
3346 result = results[i];
3348 var matched = String(result[0]);
3349 var position = max$2(min$2(toInteger(result.index), S.length), 0);
3351 // NOTE: This is equivalent to
3352 // captures = result.slice(1).map(maybeToString)
3353 // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
3354 // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
3355 // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
3356 for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
3357 var namedCaptures = result.groups;
3358 if (functionalReplace) {
3359 var replacerArgs = [matched].concat(captures, position, S);
3360 if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
3361 var replacement = String(replaceValue.apply(undefined, replacerArgs));
3363 replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
3365 if (position >= nextSourcePosition) {
3366 accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
3367 nextSourcePosition = position + matched.length;
3370 return accumulatedResult + S.slice(nextSourcePosition);
3374 // https://tc39.github.io/ecma262/#sec-getsubstitution
3375 function getSubstitution(matched, str, position, captures, namedCaptures, replacement) {
3376 var tailPos = position + matched.length;
3377 var m = captures.length;
3378 var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
3379 if (namedCaptures !== undefined) {
3380 namedCaptures = toObject(namedCaptures);
3381 symbols = SUBSTITUTION_SYMBOLS;
3383 return nativeReplace.call(replacement, symbols, function (match, ch) {
3385 switch (ch.charAt(0)) {
3386 case '$': return '$';
3387 case '&': return matched;
3388 case '`': return str.slice(0, position);
3389 case "'": return str.slice(tailPos);
3391 capture = namedCaptures[ch.slice(1, -1)];
3395 if (n === 0) return match;
3397 var f = floor$2(n / 10);
3398 if (f === 0) return match;
3399 if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
3402 capture = captures[n - 1];
3404 return capture === undefined ? '' : capture;
3409 var MATCH = wellKnownSymbol('match');
3411 // `IsRegExp` abstract operation
3412 // https://tc39.github.io/ecma262/#sec-isregexp
3413 var isRegexp = function (it) {
3415 return isObject(it) && ((isRegExp = it[MATCH]) !== undefined ? !!isRegExp : classofRaw(it) == 'RegExp');
3418 var arrayPush = [].push;
3419 var min$3 = Math.min;
3420 var MAX_UINT32 = 0xFFFFFFFF;
3422 // babel-minify transpiles RegExp('x', 'y') -> /x/y and it causes SyntaxError
3423 var SUPPORTS_Y = !fails(function () { return !RegExp(MAX_UINT32, 'y'); });
3426 fixRegexpWellKnownSymbolLogic('split', 2, function (SPLIT, nativeSplit, maybeCallNative) {
3429 'abbc'.split(/(b)*/)[1] == 'c' ||
3430 'test'.split(/(?:)/, -1).length != 4 ||
3431 'ab'.split(/(?:ab)*/).length != 2 ||
3432 '.'.split(/(.?)(.?)/).length != 4 ||
3433 '.'.split(/()()/).length > 1 ||
3434 ''.split(/.?/).length
3436 // based on es5-shim implementation, need to rework it
3437 internalSplit = function (separator, limit) {
3438 var string = String(requireObjectCoercible(this));
3439 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
3440 if (lim === 0) return [];
3441 if (separator === undefined) return [string];
3442 // If `separator` is not a regex, use native split
3443 if (!isRegexp(separator)) {
3444 return nativeSplit.call(string, separator, lim);
3447 var flags = (separator.ignoreCase ? 'i' : '') +
3448 (separator.multiline ? 'm' : '') +
3449 (separator.unicode ? 'u' : '') +
3450 (separator.sticky ? 'y' : '');
3451 var lastLastIndex = 0;
3452 // Make `global` and avoid `lastIndex` issues by working with a copy
3453 var separatorCopy = new RegExp(separator.source, flags + 'g');
3454 var match, lastIndex, lastLength;
3455 while (match = regexpExec.call(separatorCopy, string)) {
3456 lastIndex = separatorCopy.lastIndex;
3457 if (lastIndex > lastLastIndex) {
3458 output.push(string.slice(lastLastIndex, match.index));
3459 if (match.length > 1 && match.index < string.length) arrayPush.apply(output, match.slice(1));
3460 lastLength = match[0].length;
3461 lastLastIndex = lastIndex;
3462 if (output.length >= lim) break;
3464 if (separatorCopy.lastIndex === match.index) separatorCopy.lastIndex++; // Avoid an infinite loop
3466 if (lastLastIndex === string.length) {
3467 if (lastLength || !separatorCopy.test('')) output.push('');
3468 } else output.push(string.slice(lastLastIndex));
3469 return output.length > lim ? output.slice(0, lim) : output;
3472 } else if ('0'.split(undefined, 0).length) {
3473 internalSplit = function (separator, limit) {
3474 return separator === undefined && limit === 0 ? [] : nativeSplit.call(this, separator, limit);
3476 } else internalSplit = nativeSplit;
3479 // `String.prototype.split` method
3480 // https://tc39.github.io/ecma262/#sec-string.prototype.split
3481 function split(separator, limit) {
3482 var O = requireObjectCoercible(this);
3483 var splitter = separator == undefined ? undefined : separator[SPLIT];
3484 return splitter !== undefined
3485 ? splitter.call(separator, O, limit)
3486 : internalSplit.call(String(O), separator, limit);
3488 // `RegExp.prototype[@@split]` method
3489 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@split
3491 // NOTE: This cannot be properly polyfilled in engines that don't support
3493 function (regexp, limit) {
3494 var res = maybeCallNative(internalSplit, regexp, this, limit, internalSplit !== nativeSplit);
3495 if (res.done) return res.value;
3497 var rx = anObject(regexp);
3498 var S = String(this);
3499 var C = speciesConstructor(rx, RegExp);
3501 var unicodeMatching = rx.unicode;
3502 var flags = (rx.ignoreCase ? 'i' : '') +
3503 (rx.multiline ? 'm' : '') +
3504 (rx.unicode ? 'u' : '') +
3505 (SUPPORTS_Y ? 'y' : 'g');
3507 // ^(? + rx + ) is needed, in combination with some S slicing, to
3508 // simulate the 'y' flag.
3509 var splitter = new C(SUPPORTS_Y ? rx : '^(?:' + rx.source + ')', flags);
3510 var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
3511 if (lim === 0) return [];
3512 if (S.length === 0) return regexpExecAbstract(splitter, S) === null ? [S] : [];
3516 while (q < S.length) {
3517 splitter.lastIndex = SUPPORTS_Y ? q : 0;
3518 var z = regexpExecAbstract(splitter, SUPPORTS_Y ? S : S.slice(q));
3522 (e = min$3(toLength(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p
3524 q = advanceStringIndex(S, q, unicodeMatching);
3526 A.push(S.slice(p, q));
3527 if (A.length === lim) return A;
3528 for (var i = 1; i <= z.length - 1; i++) {
3530 if (A.length === lim) return A;
3541 // a string of all valid unicode whitespaces
3542 // eslint-disable-next-line max-len
3543 var whitespaces = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
3545 var whitespace = '[' + whitespaces + ']';
3546 var ltrim = RegExp('^' + whitespace + whitespace + '*');
3547 var rtrim = RegExp(whitespace + whitespace + '*$');
3549 // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation
3550 var createMethod$3 = function (TYPE) {
3551 return function ($this) {
3552 var string = String(requireObjectCoercible($this));
3553 if (TYPE & 1) string = string.replace(ltrim, '');
3554 if (TYPE & 2) string = string.replace(rtrim, '');
3560 // `String.prototype.{ trimLeft, trimStart }` methods
3561 // https://tc39.github.io/ecma262/#sec-string.prototype.trimstart
3562 start: createMethod$3(1),
3563 // `String.prototype.{ trimRight, trimEnd }` methods
3564 // https://tc39.github.io/ecma262/#sec-string.prototype.trimend
3565 end: createMethod$3(2),
3566 // `String.prototype.trim` method
3567 // https://tc39.github.io/ecma262/#sec-string.prototype.trim
3568 trim: createMethod$3(3)
3571 var non = '\u200B\u0085\u180E';
3573 // check that a method works with the correct list
3574 // of whitespaces and has a correct name
3575 var stringTrimForced = function (METHOD_NAME) {
3576 return fails(function () {
3577 return !!whitespaces[METHOD_NAME]() || non[METHOD_NAME]() != non || whitespaces[METHOD_NAME].name !== METHOD_NAME;
3581 var $trim = stringTrim.trim;
3584 // `String.prototype.trim` method
3585 // https://tc39.github.io/ecma262/#sec-string.prototype.trim
3586 _export({ target: 'String', proto: true, forced: stringTrimForced('trim') }, {
3587 trim: function trim() {
3592 /* eslint-disable no-new */
3596 var NATIVE_ARRAY_BUFFER_VIEWS$2 = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3598 var ArrayBuffer$3 = global_1.ArrayBuffer;
3599 var Int8Array$2 = global_1.Int8Array;
3601 var typedArrayConstructorsRequireWrappers = !NATIVE_ARRAY_BUFFER_VIEWS$2 || !fails(function () {
3603 }) || !fails(function () {
3604 new Int8Array$2(-1);
3605 }) || !checkCorrectnessOfIteration(function (iterable) {
3607 new Int8Array$2(null);
3608 new Int8Array$2(1.5);
3609 new Int8Array$2(iterable);
3610 }, true) || fails(function () {
3611 // Safari (11+) bug - a reason why even Safari 13 should load a typed array polyfill
3612 return new Int8Array$2(new ArrayBuffer$3(2), 1, undefined).length !== 1;
3615 var toPositiveInteger = function (it) {
3616 var result = toInteger(it);
3617 if (result < 0) throw RangeError("The argument can't be less than 0");
3621 var toOffset = function (it, BYTES) {
3622 var offset = toPositiveInteger(it);
3623 if (offset % BYTES) throw RangeError('Wrong offset');
3627 var aTypedArrayConstructor$1 = arrayBufferViewCore.aTypedArrayConstructor;
3629 var typedArrayFrom = function from(source /* , mapfn, thisArg */) {
3630 var O = toObject(source);
3631 var argumentsLength = arguments.length;
3632 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
3633 var mapping = mapfn !== undefined;
3634 var iteratorMethod = getIteratorMethod(O);
3635 var i, length, result, step, iterator, next;
3636 if (iteratorMethod != undefined && !isArrayIteratorMethod(iteratorMethod)) {
3637 iterator = iteratorMethod.call(O);
3638 next = iterator.next;
3640 while (!(step = next.call(iterator)).done) {
3644 if (mapping && argumentsLength > 2) {
3645 mapfn = functionBindContext(mapfn, arguments[2], 2);
3647 length = toLength(O.length);
3648 result = new (aTypedArrayConstructor$1(this))(length);
3649 for (i = 0; length > i; i++) {
3650 result[i] = mapping ? mapfn(O[i], i) : O[i];
3655 // makes subclassing work correct for wrapped built-ins
3656 var inheritIfRequired = function ($this, dummy, Wrapper) {
3657 var NewTarget, NewTargetPrototype;
3659 // it can work only with native `setPrototypeOf`
3660 objectSetPrototypeOf &&
3661 // we haven't completely correct pre-ES6 way for getting `new.target`, so use this
3662 typeof (NewTarget = dummy.constructor) == 'function' &&
3663 NewTarget !== Wrapper &&
3664 isObject(NewTargetPrototype = NewTarget.prototype) &&
3665 NewTargetPrototype !== Wrapper.prototype
3666 ) objectSetPrototypeOf($this, NewTargetPrototype);
3670 var typedArrayConstructor = createCommonjsModule(function (module) {
3689 var getOwnPropertyNames = objectGetOwnPropertyNames.f;
3691 var forEach = arrayIteration.forEach;
3698 var getInternalState = internalState.get;
3699 var setInternalState = internalState.set;
3700 var nativeDefineProperty = objectDefineProperty.f;
3701 var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
3702 var round = Math.round;
3703 var RangeError = global_1.RangeError;
3704 var ArrayBuffer = arrayBuffer.ArrayBuffer;
3705 var DataView = arrayBuffer.DataView;
3706 var NATIVE_ARRAY_BUFFER_VIEWS = arrayBufferViewCore.NATIVE_ARRAY_BUFFER_VIEWS;
3707 var TYPED_ARRAY_TAG = arrayBufferViewCore.TYPED_ARRAY_TAG;
3708 var TypedArray = arrayBufferViewCore.TypedArray;
3709 var TypedArrayPrototype = arrayBufferViewCore.TypedArrayPrototype;
3710 var aTypedArrayConstructor = arrayBufferViewCore.aTypedArrayConstructor;
3711 var isTypedArray = arrayBufferViewCore.isTypedArray;
3712 var BYTES_PER_ELEMENT = 'BYTES_PER_ELEMENT';
3713 var WRONG_LENGTH = 'Wrong length';
3715 var fromList = function (C, list) {
3717 var length = list.length;
3718 var result = new (aTypedArrayConstructor(C))(length);
3719 while (length > index) result[index] = list[index++];
3723 var addGetter = function (it, key) {
3724 nativeDefineProperty(it, key, { get: function () {
3725 return getInternalState(this)[key];
3729 var isArrayBuffer = function (it) {
3731 return it instanceof ArrayBuffer || (klass = classof(it)) == 'ArrayBuffer' || klass == 'SharedArrayBuffer';
3734 var isTypedArrayIndex = function (target, key) {
3735 return isTypedArray(target)
3736 && typeof key != 'symbol'
3738 && String(+key) == String(key);
3741 var wrappedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(target, key) {
3742 return isTypedArrayIndex(target, key = toPrimitive(key, true))
3743 ? createPropertyDescriptor(2, target[key])
3744 : nativeGetOwnPropertyDescriptor(target, key);
3747 var wrappedDefineProperty = function defineProperty(target, key, descriptor) {
3748 if (isTypedArrayIndex(target, key = toPrimitive(key, true))
3749 && isObject(descriptor)
3750 && has(descriptor, 'value')
3751 && !has(descriptor, 'get')
3752 && !has(descriptor, 'set')
3753 // TODO: add validation descriptor w/o calling accessors
3754 && !descriptor.configurable
3755 && (!has(descriptor, 'writable') || descriptor.writable)
3756 && (!has(descriptor, 'enumerable') || descriptor.enumerable)
3758 target[key] = descriptor.value;
3760 } return nativeDefineProperty(target, key, descriptor);
3764 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3765 objectGetOwnPropertyDescriptor.f = wrappedGetOwnPropertyDescriptor;
3766 objectDefineProperty.f = wrappedDefineProperty;
3767 addGetter(TypedArrayPrototype, 'buffer');
3768 addGetter(TypedArrayPrototype, 'byteOffset');
3769 addGetter(TypedArrayPrototype, 'byteLength');
3770 addGetter(TypedArrayPrototype, 'length');
3773 _export({ target: 'Object', stat: true, forced: !NATIVE_ARRAY_BUFFER_VIEWS }, {
3774 getOwnPropertyDescriptor: wrappedGetOwnPropertyDescriptor,
3775 defineProperty: wrappedDefineProperty
3778 module.exports = function (TYPE, wrapper, CLAMPED) {
3779 var BYTES = TYPE.match(/\d+$/)[0] / 8;
3780 var CONSTRUCTOR_NAME = TYPE + (CLAMPED ? 'Clamped' : '') + 'Array';
3781 var GETTER = 'get' + TYPE;
3782 var SETTER = 'set' + TYPE;
3783 var NativeTypedArrayConstructor = global_1[CONSTRUCTOR_NAME];
3784 var TypedArrayConstructor = NativeTypedArrayConstructor;
3785 var TypedArrayConstructorPrototype = TypedArrayConstructor && TypedArrayConstructor.prototype;
3788 var getter = function (that, index) {
3789 var data = getInternalState(that);
3790 return data.view[GETTER](index * BYTES + data.byteOffset, true);
3793 var setter = function (that, index, value) {
3794 var data = getInternalState(that);
3795 if (CLAMPED) value = (value = round(value)) < 0 ? 0 : value > 0xFF ? 0xFF : value & 0xFF;
3796 data.view[SETTER](index * BYTES + data.byteOffset, value, true);
3799 var addElement = function (that, index) {
3800 nativeDefineProperty(that, index, {
3802 return getter(this, index);
3804 set: function (value) {
3805 return setter(this, index, value);
3811 if (!NATIVE_ARRAY_BUFFER_VIEWS) {
3812 TypedArrayConstructor = wrapper(function (that, data, offset, $length) {
3813 anInstance(that, TypedArrayConstructor, CONSTRUCTOR_NAME);
3816 var buffer, byteLength, length;
3817 if (!isObject(data)) {
3818 length = toIndex(data);
3819 byteLength = length * BYTES;
3820 buffer = new ArrayBuffer(byteLength);
3821 } else if (isArrayBuffer(data)) {
3823 byteOffset = toOffset(offset, BYTES);
3824 var $len = data.byteLength;
3825 if ($length === undefined) {
3826 if ($len % BYTES) throw RangeError(WRONG_LENGTH);
3827 byteLength = $len - byteOffset;
3828 if (byteLength < 0) throw RangeError(WRONG_LENGTH);
3830 byteLength = toLength($length) * BYTES;
3831 if (byteLength + byteOffset > $len) throw RangeError(WRONG_LENGTH);
3833 length = byteLength / BYTES;
3834 } else if (isTypedArray(data)) {
3835 return fromList(TypedArrayConstructor, data);
3837 return typedArrayFrom.call(TypedArrayConstructor, data);
3839 setInternalState(that, {
3841 byteOffset: byteOffset,
3842 byteLength: byteLength,
3844 view: new DataView(buffer)
3846 while (index < length) addElement(that, index++);
3849 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3850 TypedArrayConstructorPrototype = TypedArrayConstructor.prototype = objectCreate(TypedArrayPrototype);
3851 } else if (typedArrayConstructorsRequireWrappers) {
3852 TypedArrayConstructor = wrapper(function (dummy, data, typedArrayOffset, $length) {
3853 anInstance(dummy, TypedArrayConstructor, CONSTRUCTOR_NAME);
3854 return inheritIfRequired(function () {
3855 if (!isObject(data)) return new NativeTypedArrayConstructor(toIndex(data));
3856 if (isArrayBuffer(data)) return $length !== undefined
3857 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES), $length)
3858 : typedArrayOffset !== undefined
3859 ? new NativeTypedArrayConstructor(data, toOffset(typedArrayOffset, BYTES))
3860 : new NativeTypedArrayConstructor(data);
3861 if (isTypedArray(data)) return fromList(TypedArrayConstructor, data);
3862 return typedArrayFrom.call(TypedArrayConstructor, data);
3863 }(), dummy, TypedArrayConstructor);
3866 if (objectSetPrototypeOf) objectSetPrototypeOf(TypedArrayConstructor, TypedArray);
3867 forEach(getOwnPropertyNames(NativeTypedArrayConstructor), function (key) {
3868 if (!(key in TypedArrayConstructor)) {
3869 createNonEnumerableProperty(TypedArrayConstructor, key, NativeTypedArrayConstructor[key]);
3872 TypedArrayConstructor.prototype = TypedArrayConstructorPrototype;
3875 if (TypedArrayConstructorPrototype.constructor !== TypedArrayConstructor) {
3876 createNonEnumerableProperty(TypedArrayConstructorPrototype, 'constructor', TypedArrayConstructor);
3879 if (TYPED_ARRAY_TAG) {
3880 createNonEnumerableProperty(TypedArrayConstructorPrototype, TYPED_ARRAY_TAG, CONSTRUCTOR_NAME);
3883 exported[CONSTRUCTOR_NAME] = TypedArrayConstructor;
3886 global: true, forced: TypedArrayConstructor != NativeTypedArrayConstructor, sham: !NATIVE_ARRAY_BUFFER_VIEWS
3889 if (!(BYTES_PER_ELEMENT in TypedArrayConstructor)) {
3890 createNonEnumerableProperty(TypedArrayConstructor, BYTES_PER_ELEMENT, BYTES);
3893 if (!(BYTES_PER_ELEMENT in TypedArrayConstructorPrototype)) {
3894 createNonEnumerableProperty(TypedArrayConstructorPrototype, BYTES_PER_ELEMENT, BYTES);
3897 setSpecies(CONSTRUCTOR_NAME);
3899 } else module.exports = function () { /* empty */ };
3902 // `Uint8Array` constructor
3903 // https://tc39.github.io/ecma262/#sec-typedarray-objects
3904 typedArrayConstructor('Uint8', function (init) {
3905 return function Uint8Array(data, byteOffset, length) {
3906 return init(this, data, byteOffset, length);
3910 var min$4 = Math.min;
3912 // `Array.prototype.copyWithin` method implementation
3913 // https://tc39.github.io/ecma262/#sec-array.prototype.copywithin
3914 var arrayCopyWithin = [].copyWithin || function copyWithin(target /* = 0 */, start /* = 0, end = @length */) {
3915 var O = toObject(this);
3916 var len = toLength(O.length);
3917 var to = toAbsoluteIndex(target, len);
3918 var from = toAbsoluteIndex(start, len);
3919 var end = arguments.length > 2 ? arguments[2] : undefined;
3920 var count = min$4((end === undefined ? len : toAbsoluteIndex(end, len)) - from, len - to);
3922 if (from < to && to < from + count) {
3927 while (count-- > 0) {
3928 if (from in O) O[to] = O[from];
3935 var aTypedArray$1 = arrayBufferViewCore.aTypedArray;
3936 var exportTypedArrayMethod$1 = arrayBufferViewCore.exportTypedArrayMethod;
3938 // `%TypedArray%.prototype.copyWithin` method
3939 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.copywithin
3940 exportTypedArrayMethod$1('copyWithin', function copyWithin(target, start /* , end */) {
3941 return arrayCopyWithin.call(aTypedArray$1(this), target, start, arguments.length > 2 ? arguments[2] : undefined);
3944 var $every = arrayIteration.every;
3946 var aTypedArray$2 = arrayBufferViewCore.aTypedArray;
3947 var exportTypedArrayMethod$2 = arrayBufferViewCore.exportTypedArrayMethod;
3949 // `%TypedArray%.prototype.every` method
3950 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.every
3951 exportTypedArrayMethod$2('every', function every(callbackfn /* , thisArg */) {
3952 return $every(aTypedArray$2(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3955 var aTypedArray$3 = arrayBufferViewCore.aTypedArray;
3956 var exportTypedArrayMethod$3 = arrayBufferViewCore.exportTypedArrayMethod;
3958 // `%TypedArray%.prototype.fill` method
3959 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.fill
3960 // eslint-disable-next-line no-unused-vars
3961 exportTypedArrayMethod$3('fill', function fill(value /* , start, end */) {
3962 return arrayFill.apply(aTypedArray$3(this), arguments);
3965 var $filter = arrayIteration.filter;
3968 var aTypedArray$4 = arrayBufferViewCore.aTypedArray;
3969 var aTypedArrayConstructor$2 = arrayBufferViewCore.aTypedArrayConstructor;
3970 var exportTypedArrayMethod$4 = arrayBufferViewCore.exportTypedArrayMethod;
3972 // `%TypedArray%.prototype.filter` method
3973 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.filter
3974 exportTypedArrayMethod$4('filter', function filter(callbackfn /* , thisArg */) {
3975 var list = $filter(aTypedArray$4(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
3976 var C = speciesConstructor(this, this.constructor);
3978 var length = list.length;
3979 var result = new (aTypedArrayConstructor$2(C))(length);
3980 while (length > index) result[index] = list[index++];
3984 var $find = arrayIteration.find;
3986 var aTypedArray$5 = arrayBufferViewCore.aTypedArray;
3987 var exportTypedArrayMethod$5 = arrayBufferViewCore.exportTypedArrayMethod;
3989 // `%TypedArray%.prototype.find` method
3990 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.find
3991 exportTypedArrayMethod$5('find', function find(predicate /* , thisArg */) {
3992 return $find(aTypedArray$5(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
3995 var $findIndex = arrayIteration.findIndex;
3997 var aTypedArray$6 = arrayBufferViewCore.aTypedArray;
3998 var exportTypedArrayMethod$6 = arrayBufferViewCore.exportTypedArrayMethod;
4000 // `%TypedArray%.prototype.findIndex` method
4001 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.findindex
4002 exportTypedArrayMethod$6('findIndex', function findIndex(predicate /* , thisArg */) {
4003 return $findIndex(aTypedArray$6(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
4006 var $forEach$2 = arrayIteration.forEach;
4008 var aTypedArray$7 = arrayBufferViewCore.aTypedArray;
4009 var exportTypedArrayMethod$7 = arrayBufferViewCore.exportTypedArrayMethod;
4011 // `%TypedArray%.prototype.forEach` method
4012 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.foreach
4013 exportTypedArrayMethod$7('forEach', function forEach(callbackfn /* , thisArg */) {
4014 $forEach$2(aTypedArray$7(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
4017 var $includes = arrayIncludes.includes;
4019 var aTypedArray$8 = arrayBufferViewCore.aTypedArray;
4020 var exportTypedArrayMethod$8 = arrayBufferViewCore.exportTypedArrayMethod;
4022 // `%TypedArray%.prototype.includes` method
4023 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.includes
4024 exportTypedArrayMethod$8('includes', function includes(searchElement /* , fromIndex */) {
4025 return $includes(aTypedArray$8(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
4028 var $indexOf$1 = arrayIncludes.indexOf;
4030 var aTypedArray$9 = arrayBufferViewCore.aTypedArray;
4031 var exportTypedArrayMethod$9 = arrayBufferViewCore.exportTypedArrayMethod;
4033 // `%TypedArray%.prototype.indexOf` method
4034 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.indexof
4035 exportTypedArrayMethod$9('indexOf', function indexOf(searchElement /* , fromIndex */) {
4036 return $indexOf$1(aTypedArray$9(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
4039 var ITERATOR$5 = wellKnownSymbol('iterator');
4040 var Uint8Array$1 = global_1.Uint8Array;
4041 var arrayValues = es_array_iterator.values;
4042 var arrayKeys = es_array_iterator.keys;
4043 var arrayEntries = es_array_iterator.entries;
4044 var aTypedArray$a = arrayBufferViewCore.aTypedArray;
4045 var exportTypedArrayMethod$a = arrayBufferViewCore.exportTypedArrayMethod;
4046 var nativeTypedArrayIterator = Uint8Array$1 && Uint8Array$1.prototype[ITERATOR$5];
4048 var CORRECT_ITER_NAME = !!nativeTypedArrayIterator
4049 && (nativeTypedArrayIterator.name == 'values' || nativeTypedArrayIterator.name == undefined);
4051 var typedArrayValues = function values() {
4052 return arrayValues.call(aTypedArray$a(this));
4055 // `%TypedArray%.prototype.entries` method
4056 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.entries
4057 exportTypedArrayMethod$a('entries', function entries() {
4058 return arrayEntries.call(aTypedArray$a(this));
4060 // `%TypedArray%.prototype.keys` method
4061 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.keys
4062 exportTypedArrayMethod$a('keys', function keys() {
4063 return arrayKeys.call(aTypedArray$a(this));
4065 // `%TypedArray%.prototype.values` method
4066 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.values
4067 exportTypedArrayMethod$a('values', typedArrayValues, !CORRECT_ITER_NAME);
4068 // `%TypedArray%.prototype[@@iterator]` method
4069 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype-@@iterator
4070 exportTypedArrayMethod$a(ITERATOR$5, typedArrayValues, !CORRECT_ITER_NAME);
4072 var aTypedArray$b = arrayBufferViewCore.aTypedArray;
4073 var exportTypedArrayMethod$b = arrayBufferViewCore.exportTypedArrayMethod;
4074 var $join = [].join;
4076 // `%TypedArray%.prototype.join` method
4077 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.join
4078 // eslint-disable-next-line no-unused-vars
4079 exportTypedArrayMethod$b('join', function join(separator) {
4080 return $join.apply(aTypedArray$b(this), arguments);
4083 var min$5 = Math.min;
4084 var nativeLastIndexOf = [].lastIndexOf;
4085 var NEGATIVE_ZERO$1 = !!nativeLastIndexOf && 1 / [1].lastIndexOf(1, -0) < 0;
4086 var STRICT_METHOD$3 = arrayMethodIsStrict('lastIndexOf');
4087 // For preventing possible almost infinite loop in non-standard implementations, test the forward version of the method
4088 var USES_TO_LENGTH$4 = arrayMethodUsesToLength('indexOf', { ACCESSORS: true, 1: 0 });
4089 var FORCED$1 = NEGATIVE_ZERO$1 || !STRICT_METHOD$3 || !USES_TO_LENGTH$4;
4091 // `Array.prototype.lastIndexOf` method implementation
4092 // https://tc39.github.io/ecma262/#sec-array.prototype.lastindexof
4093 var arrayLastIndexOf = FORCED$1 ? function lastIndexOf(searchElement /* , fromIndex = @[*-1] */) {
4095 if (NEGATIVE_ZERO$1) return nativeLastIndexOf.apply(this, arguments) || 0;
4096 var O = toIndexedObject(this);
4097 var length = toLength(O.length);
4098 var index = length - 1;
4099 if (arguments.length > 1) index = min$5(index, toInteger(arguments[1]));
4100 if (index < 0) index = length + index;
4101 for (;index >= 0; index--) if (index in O && O[index] === searchElement) return index || 0;
4103 } : nativeLastIndexOf;
4105 var aTypedArray$c = arrayBufferViewCore.aTypedArray;
4106 var exportTypedArrayMethod$c = arrayBufferViewCore.exportTypedArrayMethod;
4108 // `%TypedArray%.prototype.lastIndexOf` method
4109 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.lastindexof
4110 // eslint-disable-next-line no-unused-vars
4111 exportTypedArrayMethod$c('lastIndexOf', function lastIndexOf(searchElement /* , fromIndex */) {
4112 return arrayLastIndexOf.apply(aTypedArray$c(this), arguments);
4115 var $map$1 = arrayIteration.map;
4118 var aTypedArray$d = arrayBufferViewCore.aTypedArray;
4119 var aTypedArrayConstructor$3 = arrayBufferViewCore.aTypedArrayConstructor;
4120 var exportTypedArrayMethod$d = arrayBufferViewCore.exportTypedArrayMethod;
4122 // `%TypedArray%.prototype.map` method
4123 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.map
4124 exportTypedArrayMethod$d('map', function map(mapfn /* , thisArg */) {
4125 return $map$1(aTypedArray$d(this), mapfn, arguments.length > 1 ? arguments[1] : undefined, function (O, length) {
4126 return new (aTypedArrayConstructor$3(speciesConstructor(O, O.constructor)))(length);
4130 // `Array.prototype.{ reduce, reduceRight }` methods implementation
4131 var createMethod$4 = function (IS_RIGHT) {
4132 return function (that, callbackfn, argumentsLength, memo) {
4133 aFunction$1(callbackfn);
4134 var O = toObject(that);
4135 var self = indexedObject(O);
4136 var length = toLength(O.length);
4137 var index = IS_RIGHT ? length - 1 : 0;
4138 var i = IS_RIGHT ? -1 : 1;
4139 if (argumentsLength < 2) while (true) {
4140 if (index in self) {
4146 if (IS_RIGHT ? index < 0 : length <= index) {
4147 throw TypeError('Reduce of empty array with no initial value');
4150 for (;IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) {
4151 memo = callbackfn(memo, self[index], index, O);
4158 // `Array.prototype.reduce` method
4159 // https://tc39.github.io/ecma262/#sec-array.prototype.reduce
4160 left: createMethod$4(false),
4161 // `Array.prototype.reduceRight` method
4162 // https://tc39.github.io/ecma262/#sec-array.prototype.reduceright
4163 right: createMethod$4(true)
4166 var $reduce = arrayReduce.left;
4168 var aTypedArray$e = arrayBufferViewCore.aTypedArray;
4169 var exportTypedArrayMethod$e = arrayBufferViewCore.exportTypedArrayMethod;
4171 // `%TypedArray%.prototype.reduce` method
4172 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduce
4173 exportTypedArrayMethod$e('reduce', function reduce(callbackfn /* , initialValue */) {
4174 return $reduce(aTypedArray$e(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
4177 var $reduceRight = arrayReduce.right;
4179 var aTypedArray$f = arrayBufferViewCore.aTypedArray;
4180 var exportTypedArrayMethod$f = arrayBufferViewCore.exportTypedArrayMethod;
4182 // `%TypedArray%.prototype.reduceRicht` method
4183 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reduceright
4184 exportTypedArrayMethod$f('reduceRight', function reduceRight(callbackfn /* , initialValue */) {
4185 return $reduceRight(aTypedArray$f(this), callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
4188 var aTypedArray$g = arrayBufferViewCore.aTypedArray;
4189 var exportTypedArrayMethod$g = arrayBufferViewCore.exportTypedArrayMethod;
4190 var floor$3 = Math.floor;
4192 // `%TypedArray%.prototype.reverse` method
4193 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.reverse
4194 exportTypedArrayMethod$g('reverse', function reverse() {
4196 var length = aTypedArray$g(that).length;
4197 var middle = floor$3(length / 2);
4200 while (index < middle) {
4201 value = that[index];
4202 that[index++] = that[--length];
4203 that[length] = value;
4207 var aTypedArray$h = arrayBufferViewCore.aTypedArray;
4208 var exportTypedArrayMethod$h = arrayBufferViewCore.exportTypedArrayMethod;
4210 var FORCED$2 = fails(function () {
4211 // eslint-disable-next-line no-undef
4212 new Int8Array(1).set({});
4215 // `%TypedArray%.prototype.set` method
4216 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.set
4217 exportTypedArrayMethod$h('set', function set(arrayLike /* , offset */) {
4218 aTypedArray$h(this);
4219 var offset = toOffset(arguments.length > 1 ? arguments[1] : undefined, 1);
4220 var length = this.length;
4221 var src = toObject(arrayLike);
4222 var len = toLength(src.length);
4224 if (len + offset > length) throw RangeError('Wrong length');
4225 while (index < len) this[offset + index] = src[index++];
4228 var aTypedArray$i = arrayBufferViewCore.aTypedArray;
4229 var aTypedArrayConstructor$4 = arrayBufferViewCore.aTypedArrayConstructor;
4230 var exportTypedArrayMethod$i = arrayBufferViewCore.exportTypedArrayMethod;
4231 var $slice = [].slice;
4233 var FORCED$3 = fails(function () {
4234 // eslint-disable-next-line no-undef
4235 new Int8Array(1).slice();
4238 // `%TypedArray%.prototype.slice` method
4239 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.slice
4240 exportTypedArrayMethod$i('slice', function slice(start, end) {
4241 var list = $slice.call(aTypedArray$i(this), start, end);
4242 var C = speciesConstructor(this, this.constructor);
4244 var length = list.length;
4245 var result = new (aTypedArrayConstructor$4(C))(length);
4246 while (length > index) result[index] = list[index++];
4250 var $some = arrayIteration.some;
4252 var aTypedArray$j = arrayBufferViewCore.aTypedArray;
4253 var exportTypedArrayMethod$j = arrayBufferViewCore.exportTypedArrayMethod;
4255 // `%TypedArray%.prototype.some` method
4256 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.some
4257 exportTypedArrayMethod$j('some', function some(callbackfn /* , thisArg */) {
4258 return $some(aTypedArray$j(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
4261 var aTypedArray$k = arrayBufferViewCore.aTypedArray;
4262 var exportTypedArrayMethod$k = arrayBufferViewCore.exportTypedArrayMethod;
4263 var $sort = [].sort;
4265 // `%TypedArray%.prototype.sort` method
4266 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.sort
4267 exportTypedArrayMethod$k('sort', function sort(comparefn) {
4268 return $sort.call(aTypedArray$k(this), comparefn);
4271 var aTypedArray$l = arrayBufferViewCore.aTypedArray;
4272 var exportTypedArrayMethod$l = arrayBufferViewCore.exportTypedArrayMethod;
4274 // `%TypedArray%.prototype.subarray` method
4275 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.subarray
4276 exportTypedArrayMethod$l('subarray', function subarray(begin, end) {
4277 var O = aTypedArray$l(this);
4278 var length = O.length;
4279 var beginIndex = toAbsoluteIndex(begin, length);
4280 return new (speciesConstructor(O, O.constructor))(
4282 O.byteOffset + beginIndex * O.BYTES_PER_ELEMENT,
4283 toLength((end === undefined ? length : toAbsoluteIndex(end, length)) - beginIndex)
4287 var Int8Array$3 = global_1.Int8Array;
4288 var aTypedArray$m = arrayBufferViewCore.aTypedArray;
4289 var exportTypedArrayMethod$m = arrayBufferViewCore.exportTypedArrayMethod;
4290 var $toLocaleString = [].toLocaleString;
4291 var $slice$1 = [].slice;
4293 // iOS Safari 6.x fails here
4294 var TO_LOCALE_STRING_BUG = !!Int8Array$3 && fails(function () {
4295 $toLocaleString.call(new Int8Array$3(1));
4298 var FORCED$4 = fails(function () {
4299 return [1, 2].toLocaleString() != new Int8Array$3([1, 2]).toLocaleString();
4300 }) || !fails(function () {
4301 Int8Array$3.prototype.toLocaleString.call([1, 2]);
4304 // `%TypedArray%.prototype.toLocaleString` method
4305 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.tolocalestring
4306 exportTypedArrayMethod$m('toLocaleString', function toLocaleString() {
4307 return $toLocaleString.apply(TO_LOCALE_STRING_BUG ? $slice$1.call(aTypedArray$m(this)) : aTypedArray$m(this), arguments);
4310 var exportTypedArrayMethod$n = arrayBufferViewCore.exportTypedArrayMethod;
4314 var Uint8Array$2 = global_1.Uint8Array;
4315 var Uint8ArrayPrototype = Uint8Array$2 && Uint8Array$2.prototype || {};
4316 var arrayToString = [].toString;
4317 var arrayJoin = [].join;
4319 if (fails(function () { arrayToString.call({}); })) {
4320 arrayToString = function toString() {
4321 return arrayJoin.call(this);
4325 var IS_NOT_ARRAY_METHOD = Uint8ArrayPrototype.toString != arrayToString;
4327 // `%TypedArray%.prototype.toString` method
4328 // https://tc39.github.io/ecma262/#sec-%typedarray%.prototype.tostring
4329 exportTypedArrayMethod$n('toString', arrayToString, IS_NOT_ARRAY_METHOD);
4331 // iterable DOM collections
4332 // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods
4333 var domIterables = {
4335 CSSStyleDeclaration: 0,
4341 DataTransferItemList: 0,
4343 HTMLAllCollection: 0,
4346 HTMLSelectElement: 0,
4351 PaintRequestList: 0,
4359 SVGTransformList: 0,
4360 SourceBufferList: 0,
4362 TextTrackCueList: 0,
4367 for (var COLLECTION_NAME in domIterables) {
4368 var Collection = global_1[COLLECTION_NAME];
4369 var CollectionPrototype = Collection && Collection.prototype;
4370 // some Chrome versions have non-configurable methods on DOMTokenList
4371 if (CollectionPrototype && CollectionPrototype.forEach !== arrayForEach) try {
4372 createNonEnumerableProperty(CollectionPrototype, 'forEach', arrayForEach);
4374 CollectionPrototype.forEach = arrayForEach;
4378 var ITERATOR$6 = wellKnownSymbol('iterator');
4379 var TO_STRING_TAG$4 = wellKnownSymbol('toStringTag');
4380 var ArrayValues = es_array_iterator.values;
4382 for (var COLLECTION_NAME$1 in domIterables) {
4383 var Collection$1 = global_1[COLLECTION_NAME$1];
4384 var CollectionPrototype$1 = Collection$1 && Collection$1.prototype;
4385 if (CollectionPrototype$1) {
4386 // some Chrome versions have non-configurable methods on DOMTokenList
4387 if (CollectionPrototype$1[ITERATOR$6] !== ArrayValues) try {
4388 createNonEnumerableProperty(CollectionPrototype$1, ITERATOR$6, ArrayValues);
4390 CollectionPrototype$1[ITERATOR$6] = ArrayValues;
4392 if (!CollectionPrototype$1[TO_STRING_TAG$4]) {
4393 createNonEnumerableProperty(CollectionPrototype$1, TO_STRING_TAG$4, COLLECTION_NAME$1);
4395 if (domIterables[COLLECTION_NAME$1]) for (var METHOD_NAME in es_array_iterator) {
4396 // some Chrome versions have non-configurable methods on DOMTokenList
4397 if (CollectionPrototype$1[METHOD_NAME] !== es_array_iterator[METHOD_NAME]) try {
4398 createNonEnumerableProperty(CollectionPrototype$1, METHOD_NAME, es_array_iterator[METHOD_NAME]);
4400 CollectionPrototype$1[METHOD_NAME] = es_array_iterator[METHOD_NAME];
4406 var slice = [].slice;
4407 var MSIE = /MSIE .\./.test(engineUserAgent); // <- dirty ie9- check
4409 var wrap$1 = function (scheduler) {
4410 return function (handler, timeout /* , ...arguments */) {
4411 var boundArgs = arguments.length > 2;
4412 var args = boundArgs ? slice.call(arguments, 2) : undefined;
4413 return scheduler(boundArgs ? function () {
4414 // eslint-disable-next-line no-new-func
4415 (typeof handler == 'function' ? handler : Function(handler)).apply(this, args);
4416 } : handler, timeout);
4420 // ie9- setTimeout & setInterval additional parameters fix
4421 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
4422 _export({ global: true, bind: true, forced: MSIE }, {
4423 // `setTimeout` method
4424 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout
4425 setTimeout: wrap$1(global_1.setTimeout),
4426 // `setInterval` method
4427 // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval
4428 setInterval: wrap$1(global_1.setInterval)
4431 var ITERATOR$7 = wellKnownSymbol('iterator');
4433 var nativeUrl = !fails(function () {
4434 var url = new URL('b?a=1&b=2&c=3', 'http://a');
4435 var searchParams = url.searchParams;
4437 url.pathname = 'c%20d';
4438 searchParams.forEach(function (value, key) {
4439 searchParams['delete']('b');
4440 result += key + value;
4442 return (isPure && !url.toJSON)
4443 || !searchParams.sort
4444 || url.href !== 'http://a/c%20d?a=1&c=3'
4445 || searchParams.get('c') !== '3'
4446 || String(new URLSearchParams('?a=1')) !== 'a=1'
4447 || !searchParams[ITERATOR$7]
4449 || new URL('https://a@b').username !== 'a'
4450 || new URLSearchParams(new URLSearchParams('a=b')).get('a') !== 'b'
4451 // not punycoded in Edge
4452 || new URL('http://тест').host !== 'xn--e1aybc'
4453 // not escaped in Chrome 62-
4454 || new URL('http://a#б').hash !== '#%D0%B1'
4455 // fails in Chrome 66-
4456 || result !== 'a1c3'
4458 || new URL('http://x', undefined).host !== 'x';
4461 var nativeAssign = Object.assign;
4462 var defineProperty$7 = Object.defineProperty;
4464 // `Object.assign` method
4465 // https://tc39.github.io/ecma262/#sec-object.assign
4466 var objectAssign = !nativeAssign || fails(function () {
4467 // should have correct order of operations (Edge bug)
4468 if (descriptors && nativeAssign({ b: 1 }, nativeAssign(defineProperty$7({}, 'a', {
4471 defineProperty$7(this, 'b', {
4476 }), { b: 2 })).b !== 1) return true;
4477 // should work with symbols and should have deterministic property order (V8 bug)
4480 // eslint-disable-next-line no-undef
4481 var symbol = Symbol();
4482 var alphabet = 'abcdefghijklmnopqrst';
4484 alphabet.split('').forEach(function (chr) { B[chr] = chr; });
4485 return nativeAssign({}, A)[symbol] != 7 || objectKeys(nativeAssign({}, B)).join('') != alphabet;
4486 }) ? function assign(target, source) { // eslint-disable-line no-unused-vars
4487 var T = toObject(target);
4488 var argumentsLength = arguments.length;
4490 var getOwnPropertySymbols = objectGetOwnPropertySymbols.f;
4491 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
4492 while (argumentsLength > index) {
4493 var S = indexedObject(arguments[index++]);
4494 var keys = getOwnPropertySymbols ? objectKeys(S).concat(getOwnPropertySymbols(S)) : objectKeys(S);
4495 var length = keys.length;
4498 while (length > j) {
4500 if (!descriptors || propertyIsEnumerable.call(S, key)) T[key] = S[key];
4505 // `Array.from` method implementation
4506 // https://tc39.github.io/ecma262/#sec-array.from
4507 var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
4508 var O = toObject(arrayLike);
4509 var C = typeof this == 'function' ? this : Array;
4510 var argumentsLength = arguments.length;
4511 var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
4512 var mapping = mapfn !== undefined;
4513 var iteratorMethod = getIteratorMethod(O);
4515 var length, result, step, iterator, next, value;
4516 if (mapping) mapfn = functionBindContext(mapfn, argumentsLength > 2 ? arguments[2] : undefined, 2);
4517 // if the target is not iterable or it's an array with the default iterator - use a simple case
4518 if (iteratorMethod != undefined && !(C == Array && isArrayIteratorMethod(iteratorMethod))) {
4519 iterator = iteratorMethod.call(O);
4520 next = iterator.next;
4522 for (;!(step = next.call(iterator)).done; index++) {
4523 value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value;
4524 createProperty(result, index, value);
4527 length = toLength(O.length);
4528 result = new C(length);
4529 for (;length > index; index++) {
4530 value = mapping ? mapfn(O[index], index) : O[index];
4531 createProperty(result, index, value);
4534 result.length = index;
4538 // based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js
4539 var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
4545 var initialBias = 72;
4546 var initialN = 128; // 0x80
4547 var delimiter = '-'; // '\x2D'
4548 var regexNonASCII = /[^\0-\u007E]/; // non-ASCII chars
4549 var regexSeparators = /[.\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
4550 var OVERFLOW_ERROR = 'Overflow: input needs wider integers to process';
4551 var baseMinusTMin = base - tMin;
4552 var floor$4 = Math.floor;
4553 var stringFromCharCode = String.fromCharCode;
4556 * Creates an array containing the numeric code points of each Unicode
4557 * character in the string. While JavaScript uses UCS-2 internally,
4558 * this function will convert a pair of surrogate halves (each of which
4559 * UCS-2 exposes as separate characters) into a single code point,
4562 var ucs2decode = function (string) {
4565 var length = string.length;
4566 while (counter < length) {
4567 var value = string.charCodeAt(counter++);
4568 if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
4569 // It's a high surrogate, and there is a next character.
4570 var extra = string.charCodeAt(counter++);
4571 if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.
4572 output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
4574 // It's an unmatched surrogate; only append this code unit, in case the
4575 // next code unit is the high surrogate of a surrogate pair.
4587 * Converts a digit/integer into a basic code point.
4589 var digitToBasic = function (digit) {
4590 // 0..25 map to ASCII a..z or A..Z
4591 // 26..35 map to ASCII 0..9
4592 return digit + 22 + 75 * (digit < 26);
4596 * Bias adaptation function as per section 3.4 of RFC 3492.
4597 * https://tools.ietf.org/html/rfc3492#section-3.4
4599 var adapt = function (delta, numPoints, firstTime) {
4601 delta = firstTime ? floor$4(delta / damp) : delta >> 1;
4602 delta += floor$4(delta / numPoints);
4603 for (; delta > baseMinusTMin * tMax >> 1; k += base) {
4604 delta = floor$4(delta / baseMinusTMin);
4606 return floor$4(k + (baseMinusTMin + 1) * delta / (delta + skew));
4610 * Converts a string of Unicode symbols (e.g. a domain name label) to a
4611 * Punycode string of ASCII-only symbols.
4613 // eslint-disable-next-line max-statements
4614 var encode = function (input) {
4617 // Convert the input in UCS-2 to an array of Unicode code points.
4618 input = ucs2decode(input);
4620 // Cache the length.
4621 var inputLength = input.length;
4623 // Initialize the state.
4626 var bias = initialBias;
4627 var i, currentValue;
4629 // Handle the basic code points.
4630 for (i = 0; i < input.length; i++) {
4631 currentValue = input[i];
4632 if (currentValue < 0x80) {
4633 output.push(stringFromCharCode(currentValue));
4637 var basicLength = output.length; // number of basic code points.
4638 var handledCPCount = basicLength; // number of code points that have been handled;
4640 // Finish the basic string with a delimiter unless it's empty.
4642 output.push(delimiter);
4645 // Main encoding loop:
4646 while (handledCPCount < inputLength) {
4647 // All non-basic code points < n have been handled already. Find the next larger one:
4649 for (i = 0; i < input.length; i++) {
4650 currentValue = input[i];
4651 if (currentValue >= n && currentValue < m) {
4656 // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, but guard against overflow.
4657 var handledCPCountPlusOne = handledCPCount + 1;
4658 if (m - n > floor$4((maxInt - delta) / handledCPCountPlusOne)) {
4659 throw RangeError(OVERFLOW_ERROR);
4662 delta += (m - n) * handledCPCountPlusOne;
4665 for (i = 0; i < input.length; i++) {
4666 currentValue = input[i];
4667 if (currentValue < n && ++delta > maxInt) {
4668 throw RangeError(OVERFLOW_ERROR);
4670 if (currentValue == n) {
4671 // Represent delta as a generalized variable-length integer.
4673 for (var k = base; /* no condition */; k += base) {
4674 var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
4676 var qMinusT = q - t;
4677 var baseMinusT = base - t;
4678 output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT)));
4679 q = floor$4(qMinusT / baseMinusT);
4682 output.push(stringFromCharCode(digitToBasic(q)));
4683 bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
4692 return output.join('');
4695 var stringPunycodeToAscii = function (input) {
4697 var labels = input.toLowerCase().replace(regexSeparators, '\u002E').split('.');
4699 for (i = 0; i < labels.length; i++) {
4701 encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label);
4703 return encoded.join('.');
4706 var getIterator = function (it) {
4707 var iteratorMethod = getIteratorMethod(it);
4708 if (typeof iteratorMethod != 'function') {
4709 throw TypeError(String(it) + ' is not iterable');
4710 } return anObject(iteratorMethod.call(it));
4713 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
4735 var $fetch$1 = getBuiltIn('fetch');
4736 var Headers = getBuiltIn('Headers');
4737 var ITERATOR$8 = wellKnownSymbol('iterator');
4738 var URL_SEARCH_PARAMS = 'URLSearchParams';
4739 var URL_SEARCH_PARAMS_ITERATOR = URL_SEARCH_PARAMS + 'Iterator';
4740 var setInternalState$5 = internalState.set;
4741 var getInternalParamsState = internalState.getterFor(URL_SEARCH_PARAMS);
4742 var getInternalIteratorState = internalState.getterFor(URL_SEARCH_PARAMS_ITERATOR);
4745 var sequences = Array(4);
4747 var percentSequence = function (bytes) {
4748 return sequences[bytes - 1] || (sequences[bytes - 1] = RegExp('((?:%[\\da-f]{2}){' + bytes + '})', 'gi'));
4751 var percentDecode = function (sequence) {
4753 return decodeURIComponent(sequence);
4759 var deserialize = function (it) {
4760 var result = it.replace(plus, ' ');
4763 return decodeURIComponent(result);
4766 result = result.replace(percentSequence(bytes--), percentDecode);
4772 var find = /[!'()~]|%20/g;
4783 var replacer = function (match) {
4784 return replace[match];
4787 var serialize = function (it) {
4788 return encodeURIComponent(it).replace(find, replacer);
4791 var parseSearchParams = function (result, query) {
4793 var attributes = query.split('&');
4795 var attribute, entry;
4796 while (index < attributes.length) {
4797 attribute = attributes[index++];
4798 if (attribute.length) {
4799 entry = attribute.split('=');
4801 key: deserialize(entry.shift()),
4802 value: deserialize(entry.join('='))
4809 var updateSearchParams = function (query) {
4810 this.entries.length = 0;
4811 parseSearchParams(this.entries, query);
4814 var validateArgumentsLength = function (passed, required) {
4815 if (passed < required) throw TypeError('Not enough arguments');
4818 var URLSearchParamsIterator = createIteratorConstructor(function Iterator(params, kind) {
4819 setInternalState$5(this, {
4820 type: URL_SEARCH_PARAMS_ITERATOR,
4821 iterator: getIterator(getInternalParamsState(params).entries),
4824 }, 'Iterator', function next() {
4825 var state = getInternalIteratorState(this);
4826 var kind = state.kind;
4827 var step = state.iterator.next();
4828 var entry = step.value;
4830 step.value = kind === 'keys' ? entry.key : kind === 'values' ? entry.value : [entry.key, entry.value];
4834 // `URLSearchParams` constructor
4835 // https://url.spec.whatwg.org/#interface-urlsearchparams
4836 var URLSearchParamsConstructor = function URLSearchParams(/* init */) {
4837 anInstance(this, URLSearchParamsConstructor, URL_SEARCH_PARAMS);
4838 var init = arguments.length > 0 ? arguments[0] : undefined;
4841 var iteratorMethod, iterator, next, step, entryIterator, entryNext, first, second, key;
4843 setInternalState$5(that, {
4844 type: URL_SEARCH_PARAMS,
4846 updateURL: function () { /* empty */ },
4847 updateSearchParams: updateSearchParams
4850 if (init !== undefined) {
4851 if (isObject(init)) {
4852 iteratorMethod = getIteratorMethod(init);
4853 if (typeof iteratorMethod === 'function') {
4854 iterator = iteratorMethod.call(init);
4855 next = iterator.next;
4856 while (!(step = next.call(iterator)).done) {
4857 entryIterator = getIterator(anObject(step.value));
4858 entryNext = entryIterator.next;
4860 (first = entryNext.call(entryIterator)).done ||
4861 (second = entryNext.call(entryIterator)).done ||
4862 !entryNext.call(entryIterator).done
4863 ) throw TypeError('Expected sequence with length 2');
4864 entries.push({ key: first.value + '', value: second.value + '' });
4866 } else for (key in init) if (has(init, key)) entries.push({ key: key, value: init[key] + '' });
4868 parseSearchParams(entries, typeof init === 'string' ? init.charAt(0) === '?' ? init.slice(1) : init : init + '');
4873 var URLSearchParamsPrototype = URLSearchParamsConstructor.prototype;
4875 redefineAll(URLSearchParamsPrototype, {
4876 // `URLSearchParams.prototype.appent` method
4877 // https://url.spec.whatwg.org/#dom-urlsearchparams-append
4878 append: function append(name, value) {
4879 validateArgumentsLength(arguments.length, 2);
4880 var state = getInternalParamsState(this);
4881 state.entries.push({ key: name + '', value: value + '' });
4884 // `URLSearchParams.prototype.delete` method
4885 // https://url.spec.whatwg.org/#dom-urlsearchparams-delete
4886 'delete': function (name) {
4887 validateArgumentsLength(arguments.length, 1);
4888 var state = getInternalParamsState(this);
4889 var entries = state.entries;
4890 var key = name + '';
4892 while (index < entries.length) {
4893 if (entries[index].key === key) entries.splice(index, 1);
4898 // `URLSearchParams.prototype.get` method
4899 // https://url.spec.whatwg.org/#dom-urlsearchparams-get
4900 get: function get(name) {
4901 validateArgumentsLength(arguments.length, 1);
4902 var entries = getInternalParamsState(this).entries;
4903 var key = name + '';
4905 for (; index < entries.length; index++) {
4906 if (entries[index].key === key) return entries[index].value;
4910 // `URLSearchParams.prototype.getAll` method
4911 // https://url.spec.whatwg.org/#dom-urlsearchparams-getall
4912 getAll: function getAll(name) {
4913 validateArgumentsLength(arguments.length, 1);
4914 var entries = getInternalParamsState(this).entries;
4915 var key = name + '';
4918 for (; index < entries.length; index++) {
4919 if (entries[index].key === key) result.push(entries[index].value);
4923 // `URLSearchParams.prototype.has` method
4924 // https://url.spec.whatwg.org/#dom-urlsearchparams-has
4925 has: function has(name) {
4926 validateArgumentsLength(arguments.length, 1);
4927 var entries = getInternalParamsState(this).entries;
4928 var key = name + '';
4930 while (index < entries.length) {
4931 if (entries[index++].key === key) return true;
4935 // `URLSearchParams.prototype.set` method
4936 // https://url.spec.whatwg.org/#dom-urlsearchparams-set
4937 set: function set(name, value) {
4938 validateArgumentsLength(arguments.length, 1);
4939 var state = getInternalParamsState(this);
4940 var entries = state.entries;
4942 var key = name + '';
4943 var val = value + '';
4946 for (; index < entries.length; index++) {
4947 entry = entries[index];
4948 if (entry.key === key) {
4949 if (found) entries.splice(index--, 1);
4956 if (!found) entries.push({ key: key, value: val });
4959 // `URLSearchParams.prototype.sort` method
4960 // https://url.spec.whatwg.org/#dom-urlsearchparams-sort
4961 sort: function sort() {
4962 var state = getInternalParamsState(this);
4963 var entries = state.entries;
4964 // Array#sort is not stable in some engines
4965 var slice = entries.slice();
4966 var entry, entriesIndex, sliceIndex;
4968 for (sliceIndex = 0; sliceIndex < slice.length; sliceIndex++) {
4969 entry = slice[sliceIndex];
4970 for (entriesIndex = 0; entriesIndex < sliceIndex; entriesIndex++) {
4971 if (entries[entriesIndex].key > entry.key) {
4972 entries.splice(entriesIndex, 0, entry);
4976 if (entriesIndex === sliceIndex) entries.push(entry);
4980 // `URLSearchParams.prototype.forEach` method
4981 forEach: function forEach(callback /* , thisArg */) {
4982 var entries = getInternalParamsState(this).entries;
4983 var boundFunction = functionBindContext(callback, arguments.length > 1 ? arguments[1] : undefined, 3);
4986 while (index < entries.length) {
4987 entry = entries[index++];
4988 boundFunction(entry.value, entry.key, this);
4991 // `URLSearchParams.prototype.keys` method
4992 keys: function keys() {
4993 return new URLSearchParamsIterator(this, 'keys');
4995 // `URLSearchParams.prototype.values` method
4996 values: function values() {
4997 return new URLSearchParamsIterator(this, 'values');
4999 // `URLSearchParams.prototype.entries` method
5000 entries: function entries() {
5001 return new URLSearchParamsIterator(this, 'entries');
5003 }, { enumerable: true });
5005 // `URLSearchParams.prototype[@@iterator]` method
5006 redefine(URLSearchParamsPrototype, ITERATOR$8, URLSearchParamsPrototype.entries);
5008 // `URLSearchParams.prototype.toString` method
5009 // https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
5010 redefine(URLSearchParamsPrototype, 'toString', function toString() {
5011 var entries = getInternalParamsState(this).entries;
5015 while (index < entries.length) {
5016 entry = entries[index++];
5017 result.push(serialize(entry.key) + '=' + serialize(entry.value));
5018 } return result.join('&');
5019 }, { enumerable: true });
5021 setToStringTag(URLSearchParamsConstructor, URL_SEARCH_PARAMS);
5023 _export({ global: true, forced: !nativeUrl }, {
5024 URLSearchParams: URLSearchParamsConstructor
5027 // Wrap `fetch` for correct work with polyfilled `URLSearchParams`
5028 // https://github.com/zloirock/core-js/issues/674
5029 if (!nativeUrl && typeof $fetch$1 == 'function' && typeof Headers == 'function') {
5030 _export({ global: true, enumerable: true, forced: true }, {
5031 fetch: function fetch(input /* , init */) {
5033 var init, body, headers;
5034 if (arguments.length > 1) {
5035 init = arguments[1];
5036 if (isObject(init)) {
5038 if (classof(body) === URL_SEARCH_PARAMS) {
5039 headers = init.headers ? new Headers(init.headers) : new Headers();
5040 if (!headers.has('content-type')) {
5041 headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
5043 init = objectCreate(init, {
5044 body: createPropertyDescriptor(0, String(body)),
5045 headers: createPropertyDescriptor(0, headers)
5050 } return $fetch$1.apply(this, args);
5055 var web_urlSearchParams = {
5056 URLSearchParams: URLSearchParamsConstructor,
5057 getState: getInternalParamsState
5060 // TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env`
5072 var codeAt = stringMultibyte.codeAt;
5078 var NativeURL = global_1.URL;
5079 var URLSearchParams$1 = web_urlSearchParams.URLSearchParams;
5080 var getInternalSearchParamsState = web_urlSearchParams.getState;
5081 var setInternalState$6 = internalState.set;
5082 var getInternalURLState = internalState.getterFor('URL');
5083 var floor$5 = Math.floor;
5084 var pow$1 = Math.pow;
5086 var INVALID_AUTHORITY = 'Invalid authority';
5087 var INVALID_SCHEME = 'Invalid scheme';
5088 var INVALID_HOST = 'Invalid host';
5089 var INVALID_PORT = 'Invalid port';
5091 var ALPHA = /[A-Za-z]/;
5092 var ALPHANUMERIC = /[\d+-.A-Za-z]/;
5094 var HEX_START = /^(0x|0X)/;
5095 var OCT = /^[0-7]+$/;
5097 var HEX = /^[\dA-Fa-f]+$/;
5098 // eslint-disable-next-line no-control-regex
5099 var FORBIDDEN_HOST_CODE_POINT = /[\u0000\u0009\u000A\u000D #%/:?@[\\]]/;
5100 // eslint-disable-next-line no-control-regex
5101 var FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\u0000\u0009\u000A\u000D #/:?@[\\]]/;
5102 // eslint-disable-next-line no-control-regex
5103 var LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\u0000-\u001F ]+|[\u0000-\u001F ]+$/g;
5104 // eslint-disable-next-line no-control-regex
5105 var TAB_AND_NEW_LINE = /[\u0009\u000A\u000D]/g;
5108 var parseHost = function (url, input) {
5109 var result, codePoints, index;
5110 if (input.charAt(0) == '[') {
5111 if (input.charAt(input.length - 1) != ']') return INVALID_HOST;
5112 result = parseIPv6(input.slice(1, -1));
5113 if (!result) return INVALID_HOST;
5116 } else if (!isSpecial(url)) {
5117 if (FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT.test(input)) return INVALID_HOST;
5119 codePoints = arrayFrom(input);
5120 for (index = 0; index < codePoints.length; index++) {
5121 result += percentEncode(codePoints[index], C0ControlPercentEncodeSet);
5125 input = stringPunycodeToAscii(input);
5126 if (FORBIDDEN_HOST_CODE_POINT.test(input)) return INVALID_HOST;
5127 result = parseIPv4(input);
5128 if (result === null) return INVALID_HOST;
5133 var parseIPv4 = function (input) {
5134 var parts = input.split('.');
5135 var partsLength, numbers, index, part, radix, number, ipv4;
5136 if (parts.length && parts[parts.length - 1] == '') {
5139 partsLength = parts.length;
5140 if (partsLength > 4) return input;
5142 for (index = 0; index < partsLength; index++) {
5143 part = parts[index];
5144 if (part == '') return input;
5146 if (part.length > 1 && part.charAt(0) == '0') {
5147 radix = HEX_START.test(part) ? 16 : 8;
5148 part = part.slice(radix == 8 ? 1 : 2);
5153 if (!(radix == 10 ? DEC : radix == 8 ? OCT : HEX).test(part)) return input;
5154 number = parseInt(part, radix);
5156 numbers.push(number);
5158 for (index = 0; index < partsLength; index++) {
5159 number = numbers[index];
5160 if (index == partsLength - 1) {
5161 if (number >= pow$1(256, 5 - partsLength)) return null;
5162 } else if (number > 255) return null;
5164 ipv4 = numbers.pop();
5165 for (index = 0; index < numbers.length; index++) {
5166 ipv4 += numbers[index] * pow$1(256, 3 - index);
5171 // eslint-disable-next-line max-statements
5172 var parseIPv6 = function (input) {
5173 var address = [0, 0, 0, 0, 0, 0, 0, 0];
5175 var compress = null;
5177 var value, length, numbersSeen, ipv4Piece, number, swaps, swap;
5179 var char = function () {
5180 return input.charAt(pointer);
5183 if (char() == ':') {
5184 if (input.charAt(1) != ':') return;
5187 compress = pieceIndex;
5190 if (pieceIndex == 8) return;
5191 if (char() == ':') {
5192 if (compress !== null) return;
5195 compress = pieceIndex;
5199 while (length < 4 && HEX.test(char())) {
5200 value = value * 16 + parseInt(char(), 16);
5204 if (char() == '.') {
5205 if (length == 0) return;
5207 if (pieceIndex > 6) return;
5211 if (numbersSeen > 0) {
5212 if (char() == '.' && numbersSeen < 4) pointer++;
5215 if (!DIGIT.test(char())) return;
5216 while (DIGIT.test(char())) {
5217 number = parseInt(char(), 10);
5218 if (ipv4Piece === null) ipv4Piece = number;
5219 else if (ipv4Piece == 0) return;
5220 else ipv4Piece = ipv4Piece * 10 + number;
5221 if (ipv4Piece > 255) return;
5224 address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece;
5226 if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++;
5228 if (numbersSeen != 4) return;
5230 } else if (char() == ':') {
5232 if (!char()) return;
5233 } else if (char()) return;
5234 address[pieceIndex++] = value;
5236 if (compress !== null) {
5237 swaps = pieceIndex - compress;
5239 while (pieceIndex != 0 && swaps > 0) {
5240 swap = address[pieceIndex];
5241 address[pieceIndex--] = address[compress + swaps - 1];
5242 address[compress + --swaps] = swap;
5244 } else if (pieceIndex != 8) return;
5248 var findLongestZeroSequence = function (ipv6) {
5249 var maxIndex = null;
5251 var currStart = null;
5254 for (; index < 8; index++) {
5255 if (ipv6[index] !== 0) {
5256 if (currLength > maxLength) {
5257 maxIndex = currStart;
5258 maxLength = currLength;
5263 if (currStart === null) currStart = index;
5267 if (currLength > maxLength) {
5268 maxIndex = currStart;
5269 maxLength = currLength;
5274 var serializeHost = function (host) {
5275 var result, index, compress, ignore0;
5277 if (typeof host == 'number') {
5279 for (index = 0; index < 4; index++) {
5280 result.unshift(host % 256);
5281 host = floor$5(host / 256);
5282 } return result.join('.');
5284 } else if (typeof host == 'object') {
5286 compress = findLongestZeroSequence(host);
5287 for (index = 0; index < 8; index++) {
5288 if (ignore0 && host[index] === 0) continue;
5289 if (ignore0) ignore0 = false;
5290 if (compress === index) {
5291 result += index ? ':' : '::';
5294 result += host[index].toString(16);
5295 if (index < 7) result += ':';
5298 return '[' + result + ']';
5302 var C0ControlPercentEncodeSet = {};
5303 var fragmentPercentEncodeSet = objectAssign({}, C0ControlPercentEncodeSet, {
5304 ' ': 1, '"': 1, '<': 1, '>': 1, '`': 1
5306 var pathPercentEncodeSet = objectAssign({}, fragmentPercentEncodeSet, {
5307 '#': 1, '?': 1, '{': 1, '}': 1
5309 var userinfoPercentEncodeSet = objectAssign({}, pathPercentEncodeSet, {
5310 '/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\': 1, ']': 1, '^': 1, '|': 1
5313 var percentEncode = function (char, set) {
5314 var code = codeAt(char, 0);
5315 return code > 0x20 && code < 0x7F && !has(set, char) ? char : encodeURIComponent(char);
5318 var specialSchemes = {
5327 var isSpecial = function (url) {
5328 return has(specialSchemes, url.scheme);
5331 var includesCredentials = function (url) {
5332 return url.username != '' || url.password != '';
5335 var cannotHaveUsernamePasswordPort = function (url) {
5336 return !url.host || url.cannotBeABaseURL || url.scheme == 'file';
5339 var isWindowsDriveLetter = function (string, normalized) {
5341 return string.length == 2 && ALPHA.test(string.charAt(0))
5342 && ((second = string.charAt(1)) == ':' || (!normalized && second == '|'));
5345 var startsWithWindowsDriveLetter = function (string) {
5347 return string.length > 1 && isWindowsDriveLetter(string.slice(0, 2)) && (
5348 string.length == 2 ||
5349 ((third = string.charAt(2)) === '/' || third === '\\' || third === '?' || third === '#')
5353 var shortenURLsPath = function (url) {
5354 var path = url.path;
5355 var pathSize = path.length;
5356 if (pathSize && (url.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) {
5361 var isSingleDot = function (segment) {
5362 return segment === '.' || segment.toLowerCase() === '%2e';
5365 var isDoubleDot = function (segment) {
5366 segment = segment.toLowerCase();
5367 return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e';
5371 var SCHEME_START = {};
5374 var SPECIAL_RELATIVE_OR_AUTHORITY = {};
5375 var PATH_OR_AUTHORITY = {};
5377 var RELATIVE_SLASH = {};
5378 var SPECIAL_AUTHORITY_SLASHES = {};
5379 var SPECIAL_AUTHORITY_IGNORE_SLASHES = {};
5385 var FILE_SLASH = {};
5387 var PATH_START = {};
5389 var CANNOT_BE_A_BASE_URL_PATH = {};
5393 // eslint-disable-next-line max-statements
5394 var parseURL = function (url, input, stateOverride, base) {
5395 var state = stateOverride || SCHEME_START;
5399 var seenBracket = false;
5400 var seenPasswordToken = false;
5401 var codePoints, char, bufferCodePoints, failure;
5403 if (!stateOverride) {
5411 url.fragment = null;
5412 url.cannotBeABaseURL = false;
5413 input = input.replace(LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, '');
5416 input = input.replace(TAB_AND_NEW_LINE, '');
5418 codePoints = arrayFrom(input);
5420 while (pointer <= codePoints.length) {
5421 char = codePoints[pointer];
5424 if (char && ALPHA.test(char)) {
5425 buffer += char.toLowerCase();
5427 } else if (!stateOverride) {
5430 } else return INVALID_SCHEME;
5434 if (char && (ALPHANUMERIC.test(char) || char == '+' || char == '-' || char == '.')) {
5435 buffer += char.toLowerCase();
5436 } else if (char == ':') {
5437 if (stateOverride && (
5438 (isSpecial(url) != has(specialSchemes, buffer)) ||
5439 (buffer == 'file' && (includesCredentials(url) || url.port !== null)) ||
5440 (url.scheme == 'file' && !url.host)
5442 url.scheme = buffer;
5443 if (stateOverride) {
5444 if (isSpecial(url) && specialSchemes[url.scheme] == url.port) url.port = null;
5448 if (url.scheme == 'file') {
5450 } else if (isSpecial(url) && base && base.scheme == url.scheme) {
5451 state = SPECIAL_RELATIVE_OR_AUTHORITY;
5452 } else if (isSpecial(url)) {
5453 state = SPECIAL_AUTHORITY_SLASHES;
5454 } else if (codePoints[pointer + 1] == '/') {
5455 state = PATH_OR_AUTHORITY;
5458 url.cannotBeABaseURL = true;
5460 state = CANNOT_BE_A_BASE_URL_PATH;
5462 } else if (!stateOverride) {
5467 } else return INVALID_SCHEME;
5471 if (!base || (base.cannotBeABaseURL && char != '#')) return INVALID_SCHEME;
5472 if (base.cannotBeABaseURL && char == '#') {
5473 url.scheme = base.scheme;
5474 url.path = base.path.slice();
5475 url.query = base.query;
5477 url.cannotBeABaseURL = true;
5481 state = base.scheme == 'file' ? FILE : RELATIVE;
5484 case SPECIAL_RELATIVE_OR_AUTHORITY:
5485 if (char == '/' && codePoints[pointer + 1] == '/') {
5486 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5493 case PATH_OR_AUTHORITY:
5503 url.scheme = base.scheme;
5505 url.username = base.username;
5506 url.password = base.password;
5507 url.host = base.host;
5508 url.port = base.port;
5509 url.path = base.path.slice();
5510 url.query = base.query;
5511 } else if (char == '/' || (char == '\\' && isSpecial(url))) {
5512 state = RELATIVE_SLASH;
5513 } else if (char == '?') {
5514 url.username = base.username;
5515 url.password = base.password;
5516 url.host = base.host;
5517 url.port = base.port;
5518 url.path = base.path.slice();
5521 } else if (char == '#') {
5522 url.username = base.username;
5523 url.password = base.password;
5524 url.host = base.host;
5525 url.port = base.port;
5526 url.path = base.path.slice();
5527 url.query = base.query;
5531 url.username = base.username;
5532 url.password = base.password;
5533 url.host = base.host;
5534 url.port = base.port;
5535 url.path = base.path.slice();
5541 case RELATIVE_SLASH:
5542 if (isSpecial(url) && (char == '/' || char == '\\')) {
5543 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5544 } else if (char == '/') {
5547 url.username = base.username;
5548 url.password = base.password;
5549 url.host = base.host;
5550 url.port = base.port;
5555 case SPECIAL_AUTHORITY_SLASHES:
5556 state = SPECIAL_AUTHORITY_IGNORE_SLASHES;
5557 if (char != '/' || buffer.charAt(pointer + 1) != '/') continue;
5561 case SPECIAL_AUTHORITY_IGNORE_SLASHES:
5562 if (char != '/' && char != '\\') {
5569 if (seenAt) buffer = '%40' + buffer;
5571 bufferCodePoints = arrayFrom(buffer);
5572 for (var i = 0; i < bufferCodePoints.length; i++) {
5573 var codePoint = bufferCodePoints[i];
5574 if (codePoint == ':' && !seenPasswordToken) {
5575 seenPasswordToken = true;
5578 var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet);
5579 if (seenPasswordToken) url.password += encodedCodePoints;
5580 else url.username += encodedCodePoints;
5584 char == EOF || char == '/' || char == '?' || char == '#' ||
5585 (char == '\\' && isSpecial(url))
5587 if (seenAt && buffer == '') return INVALID_AUTHORITY;
5588 pointer -= arrayFrom(buffer).length + 1;
5591 } else buffer += char;
5596 if (stateOverride && url.scheme == 'file') {
5599 } else if (char == ':' && !seenBracket) {
5600 if (buffer == '') return INVALID_HOST;
5601 failure = parseHost(url, buffer);
5602 if (failure) return failure;
5605 if (stateOverride == HOSTNAME) return;
5607 char == EOF || char == '/' || char == '?' || char == '#' ||
5608 (char == '\\' && isSpecial(url))
5610 if (isSpecial(url) && buffer == '') return INVALID_HOST;
5611 if (stateOverride && buffer == '' && (includesCredentials(url) || url.port !== null)) return;
5612 failure = parseHost(url, buffer);
5613 if (failure) return failure;
5616 if (stateOverride) return;
5619 if (char == '[') seenBracket = true;
5620 else if (char == ']') seenBracket = false;
5625 if (DIGIT.test(char)) {
5628 char == EOF || char == '/' || char == '?' || char == '#' ||
5629 (char == '\\' && isSpecial(url)) ||
5633 var port = parseInt(buffer, 10);
5634 if (port > 0xFFFF) return INVALID_PORT;
5635 url.port = (isSpecial(url) && port === specialSchemes[url.scheme]) ? null : port;
5638 if (stateOverride) return;
5641 } else return INVALID_PORT;
5645 url.scheme = 'file';
5646 if (char == '/' || char == '\\') state = FILE_SLASH;
5647 else if (base && base.scheme == 'file') {
5649 url.host = base.host;
5650 url.path = base.path.slice();
5651 url.query = base.query;
5652 } else if (char == '?') {
5653 url.host = base.host;
5654 url.path = base.path.slice();
5657 } else if (char == '#') {
5658 url.host = base.host;
5659 url.path = base.path.slice();
5660 url.query = base.query;
5664 if (!startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5665 url.host = base.host;
5666 url.path = base.path.slice();
5667 shortenURLsPath(url);
5678 if (char == '/' || char == '\\') {
5682 if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(codePoints.slice(pointer).join(''))) {
5683 if (isWindowsDriveLetter(base.path[0], true)) url.path.push(base.path[0]);
5684 else url.host = base.host;
5690 if (char == EOF || char == '/' || char == '\\' || char == '?' || char == '#') {
5691 if (!stateOverride && isWindowsDriveLetter(buffer)) {
5693 } else if (buffer == '') {
5695 if (stateOverride) return;
5698 failure = parseHost(url, buffer);
5699 if (failure) return failure;
5700 if (url.host == 'localhost') url.host = '';
5701 if (stateOverride) return;
5705 } else buffer += char;
5709 if (isSpecial(url)) {
5711 if (char != '/' && char != '\\') continue;
5712 } else if (!stateOverride && char == '?') {
5715 } else if (!stateOverride && char == '#') {
5718 } else if (char != EOF) {
5720 if (char != '/') continue;
5725 char == EOF || char == '/' ||
5726 (char == '\\' && isSpecial(url)) ||
5727 (!stateOverride && (char == '?' || char == '#'))
5729 if (isDoubleDot(buffer)) {
5730 shortenURLsPath(url);
5731 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5734 } else if (isSingleDot(buffer)) {
5735 if (char != '/' && !(char == '\\' && isSpecial(url))) {
5739 if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) {
5740 if (url.host) url.host = '';
5741 buffer = buffer.charAt(0) + ':'; // normalize windows drive letter
5743 url.path.push(buffer);
5746 if (url.scheme == 'file' && (char == EOF || char == '?' || char == '#')) {
5747 while (url.path.length > 1 && url.path[0] === '') {
5754 } else if (char == '#') {
5759 buffer += percentEncode(char, pathPercentEncodeSet);
5762 case CANNOT_BE_A_BASE_URL_PATH:
5766 } else if (char == '#') {
5769 } else if (char != EOF) {
5770 url.path[0] += percentEncode(char, C0ControlPercentEncodeSet);
5774 if (!stateOverride && char == '#') {
5777 } else if (char != EOF) {
5778 if (char == "'" && isSpecial(url)) url.query += '%27';
5779 else if (char == '#') url.query += '%23';
5780 else url.query += percentEncode(char, C0ControlPercentEncodeSet);
5784 if (char != EOF) url.fragment += percentEncode(char, fragmentPercentEncodeSet);
5792 // `URL` constructor
5793 // https://url.spec.whatwg.org/#url-class
5794 var URLConstructor = function URL(url /* , base */) {
5795 var that = anInstance(this, URLConstructor, 'URL');
5796 var base = arguments.length > 1 ? arguments[1] : undefined;
5797 var urlString = String(url);
5798 var state = setInternalState$6(that, { type: 'URL' });
5799 var baseState, failure;
5800 if (base !== undefined) {
5801 if (base instanceof URLConstructor) baseState = getInternalURLState(base);
5803 failure = parseURL(baseState = {}, String(base));
5804 if (failure) throw TypeError(failure);
5807 failure = parseURL(state, urlString, null, baseState);
5808 if (failure) throw TypeError(failure);
5809 var searchParams = state.searchParams = new URLSearchParams$1();
5810 var searchParamsState = getInternalSearchParamsState(searchParams);
5811 searchParamsState.updateSearchParams(state.query);
5812 searchParamsState.updateURL = function () {
5813 state.query = String(searchParams) || null;
5816 that.href = serializeURL.call(that);
5817 that.origin = getOrigin.call(that);
5818 that.protocol = getProtocol.call(that);
5819 that.username = getUsername.call(that);
5820 that.password = getPassword.call(that);
5821 that.host = getHost.call(that);
5822 that.hostname = getHostname.call(that);
5823 that.port = getPort.call(that);
5824 that.pathname = getPathname.call(that);
5825 that.search = getSearch.call(that);
5826 that.searchParams = getSearchParams.call(that);
5827 that.hash = getHash.call(that);
5831 var URLPrototype = URLConstructor.prototype;
5833 var serializeURL = function () {
5834 var url = getInternalURLState(this);
5835 var scheme = url.scheme;
5836 var username = url.username;
5837 var password = url.password;
5838 var host = url.host;
5839 var port = url.port;
5840 var path = url.path;
5841 var query = url.query;
5842 var fragment = url.fragment;
5843 var output = scheme + ':';
5844 if (host !== null) {
5846 if (includesCredentials(url)) {
5847 output += username + (password ? ':' + password : '') + '@';
5849 output += serializeHost(host);
5850 if (port !== null) output += ':' + port;
5851 } else if (scheme == 'file') output += '//';
5852 output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5853 if (query !== null) output += '?' + query;
5854 if (fragment !== null) output += '#' + fragment;
5858 var getOrigin = function () {
5859 var url = getInternalURLState(this);
5860 var scheme = url.scheme;
5861 var port = url.port;
5862 if (scheme == 'blob') try {
5863 return new URL(scheme.path[0]).origin;
5867 if (scheme == 'file' || !isSpecial(url)) return 'null';
5868 return scheme + '://' + serializeHost(url.host) + (port !== null ? ':' + port : '');
5871 var getProtocol = function () {
5872 return getInternalURLState(this).scheme + ':';
5875 var getUsername = function () {
5876 return getInternalURLState(this).username;
5879 var getPassword = function () {
5880 return getInternalURLState(this).password;
5883 var getHost = function () {
5884 var url = getInternalURLState(this);
5885 var host = url.host;
5886 var port = url.port;
5887 return host === null ? ''
5888 : port === null ? serializeHost(host)
5889 : serializeHost(host) + ':' + port;
5892 var getHostname = function () {
5893 var host = getInternalURLState(this).host;
5894 return host === null ? '' : serializeHost(host);
5897 var getPort = function () {
5898 var port = getInternalURLState(this).port;
5899 return port === null ? '' : String(port);
5902 var getPathname = function () {
5903 var url = getInternalURLState(this);
5904 var path = url.path;
5905 return url.cannotBeABaseURL ? path[0] : path.length ? '/' + path.join('/') : '';
5908 var getSearch = function () {
5909 var query = getInternalURLState(this).query;
5910 return query ? '?' + query : '';
5913 var getSearchParams = function () {
5914 return getInternalURLState(this).searchParams;
5917 var getHash = function () {
5918 var fragment = getInternalURLState(this).fragment;
5919 return fragment ? '#' + fragment : '';
5922 var accessorDescriptor = function (getter, setter) {
5923 return { get: getter, set: setter, configurable: true, enumerable: true };
5927 objectDefineProperties(URLPrototype, {
5928 // `URL.prototype.href` accessors pair
5929 // https://url.spec.whatwg.org/#dom-url-href
5930 href: accessorDescriptor(serializeURL, function (href) {
5931 var url = getInternalURLState(this);
5932 var urlString = String(href);
5933 var failure = parseURL(url, urlString);
5934 if (failure) throw TypeError(failure);
5935 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
5937 // `URL.prototype.origin` getter
5938 // https://url.spec.whatwg.org/#dom-url-origin
5939 origin: accessorDescriptor(getOrigin),
5940 // `URL.prototype.protocol` accessors pair
5941 // https://url.spec.whatwg.org/#dom-url-protocol
5942 protocol: accessorDescriptor(getProtocol, function (protocol) {
5943 var url = getInternalURLState(this);
5944 parseURL(url, String(protocol) + ':', SCHEME_START);
5946 // `URL.prototype.username` accessors pair
5947 // https://url.spec.whatwg.org/#dom-url-username
5948 username: accessorDescriptor(getUsername, function (username) {
5949 var url = getInternalURLState(this);
5950 var codePoints = arrayFrom(String(username));
5951 if (cannotHaveUsernamePasswordPort(url)) return;
5953 for (var i = 0; i < codePoints.length; i++) {
5954 url.username += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5957 // `URL.prototype.password` accessors pair
5958 // https://url.spec.whatwg.org/#dom-url-password
5959 password: accessorDescriptor(getPassword, function (password) {
5960 var url = getInternalURLState(this);
5961 var codePoints = arrayFrom(String(password));
5962 if (cannotHaveUsernamePasswordPort(url)) return;
5964 for (var i = 0; i < codePoints.length; i++) {
5965 url.password += percentEncode(codePoints[i], userinfoPercentEncodeSet);
5968 // `URL.prototype.host` accessors pair
5969 // https://url.spec.whatwg.org/#dom-url-host
5970 host: accessorDescriptor(getHost, function (host) {
5971 var url = getInternalURLState(this);
5972 if (url.cannotBeABaseURL) return;
5973 parseURL(url, String(host), HOST);
5975 // `URL.prototype.hostname` accessors pair
5976 // https://url.spec.whatwg.org/#dom-url-hostname
5977 hostname: accessorDescriptor(getHostname, function (hostname) {
5978 var url = getInternalURLState(this);
5979 if (url.cannotBeABaseURL) return;
5980 parseURL(url, String(hostname), HOSTNAME);
5982 // `URL.prototype.port` accessors pair
5983 // https://url.spec.whatwg.org/#dom-url-port
5984 port: accessorDescriptor(getPort, function (port) {
5985 var url = getInternalURLState(this);
5986 if (cannotHaveUsernamePasswordPort(url)) return;
5987 port = String(port);
5988 if (port == '') url.port = null;
5989 else parseURL(url, port, PORT);
5991 // `URL.prototype.pathname` accessors pair
5992 // https://url.spec.whatwg.org/#dom-url-pathname
5993 pathname: accessorDescriptor(getPathname, function (pathname) {
5994 var url = getInternalURLState(this);
5995 if (url.cannotBeABaseURL) return;
5997 parseURL(url, pathname + '', PATH_START);
5999 // `URL.prototype.search` accessors pair
6000 // https://url.spec.whatwg.org/#dom-url-search
6001 search: accessorDescriptor(getSearch, function (search) {
6002 var url = getInternalURLState(this);
6003 search = String(search);
6007 if ('?' == search.charAt(0)) search = search.slice(1);
6009 parseURL(url, search, QUERY);
6011 getInternalSearchParamsState(url.searchParams).updateSearchParams(url.query);
6013 // `URL.prototype.searchParams` getter
6014 // https://url.spec.whatwg.org/#dom-url-searchparams
6015 searchParams: accessorDescriptor(getSearchParams),
6016 // `URL.prototype.hash` accessors pair
6017 // https://url.spec.whatwg.org/#dom-url-hash
6018 hash: accessorDescriptor(getHash, function (hash) {
6019 var url = getInternalURLState(this);
6020 hash = String(hash);
6022 url.fragment = null;
6025 if ('#' == hash.charAt(0)) hash = hash.slice(1);
6027 parseURL(url, hash, FRAGMENT);
6032 // `URL.prototype.toJSON` method
6033 // https://url.spec.whatwg.org/#dom-url-tojson
6034 redefine(URLPrototype, 'toJSON', function toJSON() {
6035 return serializeURL.call(this);
6036 }, { enumerable: true });
6038 // `URL.prototype.toString` method
6039 // https://url.spec.whatwg.org/#URL-stringification-behavior
6040 redefine(URLPrototype, 'toString', function toString() {
6041 return serializeURL.call(this);
6042 }, { enumerable: true });
6045 var nativeCreateObjectURL = NativeURL.createObjectURL;
6046 var nativeRevokeObjectURL = NativeURL.revokeObjectURL;
6047 // `URL.createObjectURL` method
6048 // https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
6049 // eslint-disable-next-line no-unused-vars
6050 if (nativeCreateObjectURL) redefine(URLConstructor, 'createObjectURL', function createObjectURL(blob) {
6051 return nativeCreateObjectURL.apply(NativeURL, arguments);
6053 // `URL.revokeObjectURL` method
6054 // https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
6055 // eslint-disable-next-line no-unused-vars
6056 if (nativeRevokeObjectURL) redefine(URLConstructor, 'revokeObjectURL', function revokeObjectURL(url) {
6057 return nativeRevokeObjectURL.apply(NativeURL, arguments);
6061 setToStringTag(URLConstructor, 'URL');
6063 _export({ global: true, forced: !nativeUrl, sham: !descriptors }, {
6067 function _typeof(obj) {
6068 "@babel/helpers - typeof";
6070 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
6071 _typeof = function (obj) {
6075 _typeof = function (obj) {
6076 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
6080 return _typeof(obj);
6083 function _classCallCheck(instance, Constructor) {
6084 if (!(instance instanceof Constructor)) {
6085 throw new TypeError("Cannot call a class as a function");
6089 function _defineProperties(target, props) {
6090 for (var i = 0; i < props.length; i++) {
6091 var descriptor = props[i];
6092 descriptor.enumerable = descriptor.enumerable || false;
6093 descriptor.configurable = true;
6094 if ("value" in descriptor) descriptor.writable = true;
6095 Object.defineProperty(target, descriptor.key, descriptor);
6099 function _createClass(Constructor, protoProps, staticProps) {
6100 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
6101 if (staticProps) _defineProperties(Constructor, staticProps);
6105 function _defineProperty(obj, key, value) {
6107 Object.defineProperty(obj, key, {
6120 function _slicedToArray(arr, i) {
6121 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
6124 function _toConsumableArray(arr) {
6125 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
6128 function _arrayWithoutHoles(arr) {
6129 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
6132 function _arrayWithHoles(arr) {
6133 if (Array.isArray(arr)) return arr;
6136 function _iterableToArray(iter) {
6137 if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
6140 function _iterableToArrayLimit(arr, i) {
6141 if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
6148 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
6149 _arr.push(_s.value);
6151 if (i && _arr.length === i) break;
6158 if (!_n && _i["return"] != null) _i["return"]();
6167 function _unsupportedIterableToArray(o, minLen) {
6169 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
6170 var n = Object.prototype.toString.call(o).slice(8, -1);
6171 if (n === "Object" && o.constructor) n = o.constructor.name;
6172 if (n === "Map" || n === "Set") return Array.from(o);
6173 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
6176 function _arrayLikeToArray(arr, len) {
6177 if (len == null || len > arr.length) len = arr.length;
6179 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
6184 function _nonIterableSpread() {
6185 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
6188 function _nonIterableRest() {
6189 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
6192 function _createForOfIteratorHelper(o, allowArrayLike) {
6195 if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
6196 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
6200 var F = function () {};
6205 if (i >= o.length) return {
6220 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
6223 var normalCompletion = true,
6228 it = o[Symbol.iterator]();
6231 var step = it.next();
6232 normalCompletion = step.done;
6241 if (!normalCompletion && it.return != null) it.return();
6243 if (didErr) throw err;
6249 var global$1 = typeof globalThis !== 'undefined' && globalThis || typeof self !== 'undefined' && self || typeof global$1 !== 'undefined' && global$1;
6251 searchParams: 'URLSearchParams' in global$1,
6252 iterable: 'Symbol' in global$1 && 'iterator' in Symbol,
6253 blob: 'FileReader' in global$1 && 'Blob' in global$1 && function () {
6261 formData: 'FormData' in global$1,
6262 arrayBuffer: 'ArrayBuffer' in global$1
6265 function isDataView(obj) {
6266 return obj && DataView.prototype.isPrototypeOf(obj);
6269 if (support.arrayBuffer) {
6270 var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
6272 var isArrayBufferView = ArrayBuffer.isView || function (obj) {
6273 return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
6277 function normalizeName(name) {
6278 if (typeof name !== 'string') {
6279 name = String(name);
6282 if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
6283 throw new TypeError('Invalid character in header field name');
6286 return name.toLowerCase();
6289 function normalizeValue(value) {
6290 if (typeof value !== 'string') {
6291 value = String(value);
6295 } // Build a destructive iterator for the value list
6298 function iteratorFor(items) {
6300 next: function next() {
6301 var value = items.shift();
6303 done: value === undefined,
6309 if (support.iterable) {
6310 iterator[Symbol.iterator] = function () {
6318 function Headers$1(headers) {
6321 if (headers instanceof Headers$1) {
6322 headers.forEach(function (value, name) {
6323 this.append(name, value);
6325 } else if (Array.isArray(headers)) {
6326 headers.forEach(function (header) {
6327 this.append(header[0], header[1]);
6329 } else if (headers) {
6330 Object.getOwnPropertyNames(headers).forEach(function (name) {
6331 this.append(name, headers[name]);
6336 Headers$1.prototype.append = function (name, value) {
6337 name = normalizeName(name);
6338 value = normalizeValue(value);
6339 var oldValue = this.map[name];
6340 this.map[name] = oldValue ? oldValue + ', ' + value : value;
6343 Headers$1.prototype['delete'] = function (name) {
6344 delete this.map[normalizeName(name)];
6347 Headers$1.prototype.get = function (name) {
6348 name = normalizeName(name);
6349 return this.has(name) ? this.map[name] : null;
6352 Headers$1.prototype.has = function (name) {
6353 return this.map.hasOwnProperty(normalizeName(name));
6356 Headers$1.prototype.set = function (name, value) {
6357 this.map[normalizeName(name)] = normalizeValue(value);
6360 Headers$1.prototype.forEach = function (callback, thisArg) {
6361 for (var name in this.map) {
6362 if (this.map.hasOwnProperty(name)) {
6363 callback.call(thisArg, this.map[name], name, this);
6368 Headers$1.prototype.keys = function () {
6370 this.forEach(function (value, name) {
6373 return iteratorFor(items);
6376 Headers$1.prototype.values = function () {
6378 this.forEach(function (value) {
6381 return iteratorFor(items);
6384 Headers$1.prototype.entries = function () {
6386 this.forEach(function (value, name) {
6387 items.push([name, value]);
6389 return iteratorFor(items);
6392 if (support.iterable) {
6393 Headers$1.prototype[Symbol.iterator] = Headers$1.prototype.entries;
6396 function consumed(body) {
6397 if (body.bodyUsed) {
6398 return Promise.reject(new TypeError('Already read'));
6401 body.bodyUsed = true;
6404 function fileReaderReady(reader) {
6405 return new Promise(function (resolve, reject) {
6406 reader.onload = function () {
6407 resolve(reader.result);
6410 reader.onerror = function () {
6411 reject(reader.error);
6416 function readBlobAsArrayBuffer(blob) {
6417 var reader = new FileReader();
6418 var promise = fileReaderReady(reader);
6419 reader.readAsArrayBuffer(blob);
6423 function readBlobAsText(blob) {
6424 var reader = new FileReader();
6425 var promise = fileReaderReady(reader);
6426 reader.readAsText(blob);
6430 function readArrayBufferAsText(buf) {
6431 var view = new Uint8Array(buf);
6432 var chars = new Array(view.length);
6434 for (var i = 0; i < view.length; i++) {
6435 chars[i] = String.fromCharCode(view[i]);
6438 return chars.join('');
6441 function bufferClone(buf) {
6443 return buf.slice(0);
6445 var view = new Uint8Array(buf.byteLength);
6446 view.set(new Uint8Array(buf));
6452 this.bodyUsed = false;
6454 this._initBody = function (body) {
6456 fetch-mock wraps the Response object in an ES6 Proxy to
6457 provide useful test harness features such as flush. However, on
6458 ES5 browsers without fetch or Proxy support pollyfills must be used;
6459 the proxy-pollyfill is unable to proxy an attribute unless it exists
6460 on the object before the Proxy is created. This change ensures
6461 Response.bodyUsed exists on the instance, while maintaining the
6462 semantic of setting Request.bodyUsed in the constructor before
6463 _initBody is called.
6465 this.bodyUsed = this.bodyUsed;
6466 this._bodyInit = body;
6469 this._bodyText = '';
6470 } else if (typeof body === 'string') {
6471 this._bodyText = body;
6472 } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
6473 this._bodyBlob = body;
6474 } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
6475 this._bodyFormData = body;
6476 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6477 this._bodyText = body.toString();
6478 } else if (support.arrayBuffer && support.blob && isDataView(body)) {
6479 this._bodyArrayBuffer = bufferClone(body.buffer); // IE 10-11 can't handle a DataView body.
6481 this._bodyInit = new Blob([this._bodyArrayBuffer]);
6482 } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
6483 this._bodyArrayBuffer = bufferClone(body);
6485 this._bodyText = body = Object.prototype.toString.call(body);
6488 if (!this.headers.get('content-type')) {
6489 if (typeof body === 'string') {
6490 this.headers.set('content-type', 'text/plain;charset=UTF-8');
6491 } else if (this._bodyBlob && this._bodyBlob.type) {
6492 this.headers.set('content-type', this._bodyBlob.type);
6493 } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
6494 this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
6500 this.blob = function () {
6501 var rejected = consumed(this);
6507 if (this._bodyBlob) {
6508 return Promise.resolve(this._bodyBlob);
6509 } else if (this._bodyArrayBuffer) {
6510 return Promise.resolve(new Blob([this._bodyArrayBuffer]));
6511 } else if (this._bodyFormData) {
6512 throw new Error('could not read FormData body as blob');
6514 return Promise.resolve(new Blob([this._bodyText]));
6518 this.arrayBuffer = function () {
6519 if (this._bodyArrayBuffer) {
6520 var isConsumed = consumed(this);
6526 if (ArrayBuffer.isView(this._bodyArrayBuffer)) {
6527 return Promise.resolve(this._bodyArrayBuffer.buffer.slice(this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength));
6529 return Promise.resolve(this._bodyArrayBuffer);
6532 return this.blob().then(readBlobAsArrayBuffer);
6537 this.text = function () {
6538 var rejected = consumed(this);
6544 if (this._bodyBlob) {
6545 return readBlobAsText(this._bodyBlob);
6546 } else if (this._bodyArrayBuffer) {
6547 return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
6548 } else if (this._bodyFormData) {
6549 throw new Error('could not read FormData body as text');
6551 return Promise.resolve(this._bodyText);
6555 if (support.formData) {
6556 this.formData = function () {
6557 return this.text().then(decode);
6561 this.json = function () {
6562 return this.text().then(JSON.parse);
6566 } // HTTP methods whose capitalization should be normalized
6569 var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
6571 function normalizeMethod(method) {
6572 var upcased = method.toUpperCase();
6573 return methods.indexOf(upcased) > -1 ? upcased : method;
6576 function Request(input, options) {
6577 if (!(this instanceof Request)) {
6578 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6581 options = options || {};
6582 var body = options.body;
6584 if (input instanceof Request) {
6585 if (input.bodyUsed) {
6586 throw new TypeError('Already read');
6589 this.url = input.url;
6590 this.credentials = input.credentials;
6592 if (!options.headers) {
6593 this.headers = new Headers$1(input.headers);
6596 this.method = input.method;
6597 this.mode = input.mode;
6598 this.signal = input.signal;
6600 if (!body && input._bodyInit != null) {
6601 body = input._bodyInit;
6602 input.bodyUsed = true;
6605 this.url = String(input);
6608 this.credentials = options.credentials || this.credentials || 'same-origin';
6610 if (options.headers || !this.headers) {
6611 this.headers = new Headers$1(options.headers);
6614 this.method = normalizeMethod(options.method || this.method || 'GET');
6615 this.mode = options.mode || this.mode || null;
6616 this.signal = options.signal || this.signal;
6617 this.referrer = null;
6619 if ((this.method === 'GET' || this.method === 'HEAD') && body) {
6620 throw new TypeError('Body not allowed for GET or HEAD requests');
6623 this._initBody(body);
6625 if (this.method === 'GET' || this.method === 'HEAD') {
6626 if (options.cache === 'no-store' || options.cache === 'no-cache') {
6627 // Search for a '_' parameter in the query string
6628 var reParamSearch = /([?&])_=[^&]*/;
6630 if (reParamSearch.test(this.url)) {
6631 // If it already exists then set the value with the current time
6632 this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime());
6634 // Otherwise add a new '_' parameter to the end with the current time
6635 var reQueryString = /\?/;
6636 this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime();
6642 Request.prototype.clone = function () {
6643 return new Request(this, {
6644 body: this._bodyInit
6648 function decode(body) {
6649 var form = new FormData();
6650 body.trim().split('&').forEach(function (bytes) {
6652 var split = bytes.split('=');
6653 var name = split.shift().replace(/\+/g, ' ');
6654 var value = split.join('=').replace(/\+/g, ' ');
6655 form.append(decodeURIComponent(name), decodeURIComponent(value));
6661 function parseHeaders(rawHeaders) {
6662 var headers = new Headers$1(); // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
6663 // https://tools.ietf.org/html/rfc7230#section-3.2
6665 var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ');
6666 preProcessedHeaders.split(/\r?\n/).forEach(function (line) {
6667 var parts = line.split(':');
6668 var key = parts.shift().trim();
6671 var value = parts.join(':').trim();
6672 headers.append(key, value);
6678 Body.call(Request.prototype);
6679 function Response(bodyInit, options) {
6680 if (!(this instanceof Response)) {
6681 throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.');
6688 this.type = 'default';
6689 this.status = options.status === undefined ? 200 : options.status;
6690 this.ok = this.status >= 200 && this.status < 300;
6691 this.statusText = 'statusText' in options ? options.statusText : '';
6692 this.headers = new Headers$1(options.headers);
6693 this.url = options.url || '';
6695 this._initBody(bodyInit);
6697 Body.call(Response.prototype);
6699 Response.prototype.clone = function () {
6700 return new Response(this._bodyInit, {
6701 status: this.status,
6702 statusText: this.statusText,
6703 headers: new Headers$1(this.headers),
6708 Response.error = function () {
6709 var response = new Response(null, {
6713 response.type = 'error';
6717 var redirectStatuses = [301, 302, 303, 307, 308];
6719 Response.redirect = function (url, status) {
6720 if (redirectStatuses.indexOf(status) === -1) {
6721 throw new RangeError('Invalid status code');
6724 return new Response(null, {
6732 var DOMException$1 = global$1.DOMException;
6735 new DOMException$1();
6737 DOMException$1 = function DOMException(message, name) {
6738 this.message = message;
6740 var error = Error(message);
6741 this.stack = error.stack;
6744 DOMException$1.prototype = Object.create(Error.prototype);
6745 DOMException$1.prototype.constructor = DOMException$1;
6748 function fetch$1(input, init) {
6749 return new Promise(function (resolve, reject) {
6750 var request = new Request(input, init);
6752 if (request.signal && request.signal.aborted) {
6753 return reject(new DOMException$1('Aborted', 'AbortError'));
6756 var xhr = new XMLHttpRequest();
6758 function abortXhr() {
6762 xhr.onload = function () {
6765 statusText: xhr.statusText,
6766 headers: parseHeaders(xhr.getAllResponseHeaders() || '')
6768 options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
6769 var body = 'response' in xhr ? xhr.response : xhr.responseText;
6770 setTimeout(function () {
6771 resolve(new Response(body, options));
6775 xhr.onerror = function () {
6776 setTimeout(function () {
6777 reject(new TypeError('Network request failed'));
6781 xhr.ontimeout = function () {
6782 setTimeout(function () {
6783 reject(new TypeError('Network request failed'));
6787 xhr.onabort = function () {
6788 setTimeout(function () {
6789 reject(new DOMException$1('Aborted', 'AbortError'));
6793 function fixUrl(url) {
6795 return url === '' && global$1.location.href ? global$1.location.href : url;
6801 xhr.open(request.method, fixUrl(request.url), true);
6803 if (request.credentials === 'include') {
6804 xhr.withCredentials = true;
6805 } else if (request.credentials === 'omit') {
6806 xhr.withCredentials = false;
6809 if ('responseType' in xhr) {
6811 xhr.responseType = 'blob';
6812 } else if (support.arrayBuffer && request.headers.get('Content-Type') && request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1) {
6813 xhr.responseType = 'arraybuffer';
6817 if (init && _typeof(init.headers) === 'object' && !(init.headers instanceof Headers$1)) {
6818 Object.getOwnPropertyNames(init.headers).forEach(function (name) {
6819 xhr.setRequestHeader(name, normalizeValue(init.headers[name]));
6822 request.headers.forEach(function (value, name) {
6823 xhr.setRequestHeader(name, value);
6827 if (request.signal) {
6828 request.signal.addEventListener('abort', abortXhr);
6830 xhr.onreadystatechange = function () {
6831 // DONE (success or failure)
6832 if (xhr.readyState === 4) {
6833 request.signal.removeEventListener('abort', abortXhr);
6838 xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
6841 fetch$1.polyfill = true;
6843 if (!global$1.fetch) {
6844 global$1.fetch = fetch$1;
6845 global$1.Headers = Headers$1;
6846 global$1.Request = Request;
6847 global$1.Response = Response;
6850 // `Symbol.toStringTag` well-known symbol
6851 // https://tc39.github.io/ecma262/#sec-symbol.tostringtag
6852 defineWellKnownSymbol('toStringTag');
6854 var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport('splice');
6855 var USES_TO_LENGTH$5 = arrayMethodUsesToLength('splice', { ACCESSORS: true, 0: 0, 1: 2 });
6857 var max$3 = Math.max;
6858 var min$6 = Math.min;
6859 var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF;
6860 var MAXIMUM_ALLOWED_LENGTH_EXCEEDED = 'Maximum allowed length exceeded';
6862 // `Array.prototype.splice` method
6863 // https://tc39.github.io/ecma262/#sec-array.prototype.splice
6864 // with adding support of @@species
6865 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 || !USES_TO_LENGTH$5 }, {
6866 splice: function splice(start, deleteCount /* , ...items */) {
6867 var O = toObject(this);
6868 var len = toLength(O.length);
6869 var actualStart = toAbsoluteIndex(start, len);
6870 var argumentsLength = arguments.length;
6871 var insertCount, actualDeleteCount, A, k, from, to;
6872 if (argumentsLength === 0) {
6873 insertCount = actualDeleteCount = 0;
6874 } else if (argumentsLength === 1) {
6876 actualDeleteCount = len - actualStart;
6878 insertCount = argumentsLength - 2;
6879 actualDeleteCount = min$6(max$3(toInteger(deleteCount), 0), len - actualStart);
6881 if (len + insertCount - actualDeleteCount > MAX_SAFE_INTEGER) {
6882 throw TypeError(MAXIMUM_ALLOWED_LENGTH_EXCEEDED);
6884 A = arraySpeciesCreate(O, actualDeleteCount);
6885 for (k = 0; k < actualDeleteCount; k++) {
6886 from = actualStart + k;
6887 if (from in O) createProperty(A, k, O[from]);
6889 A.length = actualDeleteCount;
6890 if (insertCount < actualDeleteCount) {
6891 for (k = actualStart; k < len - actualDeleteCount; k++) {
6892 from = k + actualDeleteCount;
6893 to = k + insertCount;
6894 if (from in O) O[to] = O[from];
6897 for (k = len; k > len - actualDeleteCount + insertCount; k--) delete O[k - 1];
6898 } else if (insertCount > actualDeleteCount) {
6899 for (k = len - actualDeleteCount; k > actualStart; k--) {
6900 from = k + actualDeleteCount - 1;
6901 to = k + insertCount - 1;
6902 if (from in O) O[to] = O[from];
6906 for (k = 0; k < insertCount; k++) {
6907 O[k + actualStart] = arguments[k + 2];
6909 O.length = len - actualDeleteCount + insertCount;
6914 // JSON[@@toStringTag] property
6915 // https://tc39.github.io/ecma262/#sec-json-@@tostringtag
6916 setToStringTag(global_1.JSON, 'JSON', true);
6918 // Math[@@toStringTag] property
6919 // https://tc39.github.io/ecma262/#sec-math-@@tostringtag
6920 setToStringTag(Math, 'Math', true);
6922 // `Object.defineProperty` method
6923 // https://tc39.github.io/ecma262/#sec-object.defineproperty
6924 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
6925 defineProperty: objectDefineProperty.f
6928 var nativeGetOwnPropertyDescriptor$2 = objectGetOwnPropertyDescriptor.f;
6931 var FAILS_ON_PRIMITIVES$1 = fails(function () { nativeGetOwnPropertyDescriptor$2(1); });
6932 var FORCED$5 = !descriptors || FAILS_ON_PRIMITIVES$1;
6934 // `Object.getOwnPropertyDescriptor` method
6935 // https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptor
6936 _export({ target: 'Object', stat: true, forced: FORCED$5, sham: !descriptors }, {
6937 getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) {
6938 return nativeGetOwnPropertyDescriptor$2(toIndexedObject(it), key);
6942 var FAILS_ON_PRIMITIVES$2 = fails(function () { objectGetPrototypeOf(1); });
6944 // `Object.getPrototypeOf` method
6945 // https://tc39.github.io/ecma262/#sec-object.getprototypeof
6946 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$2, sham: !correctPrototypeGetter }, {
6947 getPrototypeOf: function getPrototypeOf(it) {
6948 return objectGetPrototypeOf(toObject(it));
6952 // `Object.setPrototypeOf` method
6953 // https://tc39.github.io/ecma262/#sec-object.setprototypeof
6954 _export({ target: 'Object', stat: true }, {
6955 setPrototypeOf: objectSetPrototypeOf
6958 var slice$1 = [].slice;
6961 var construct = function (C, argsLength, args) {
6962 if (!(argsLength in factories)) {
6963 for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']';
6964 // eslint-disable-next-line no-new-func
6965 factories[argsLength] = Function('C,a', 'return new C(' + list.join(',') + ')');
6966 } return factories[argsLength](C, args);
6969 // `Function.prototype.bind` method implementation
6970 // https://tc39.github.io/ecma262/#sec-function.prototype.bind
6971 var functionBind = Function.bind || function bind(that /* , ...args */) {
6972 var fn = aFunction$1(this);
6973 var partArgs = slice$1.call(arguments, 1);
6974 var boundFunction = function bound(/* args... */) {
6975 var args = partArgs.concat(slice$1.call(arguments));
6976 return this instanceof boundFunction ? construct(fn, args.length, args) : fn.apply(that, args);
6978 if (isObject(fn.prototype)) boundFunction.prototype = fn.prototype;
6979 return boundFunction;
6982 var nativeConstruct = getBuiltIn('Reflect', 'construct');
6984 // `Reflect.construct` method
6985 // https://tc39.github.io/ecma262/#sec-reflect.construct
6986 // MS Edge supports only 2 arguments and argumentsList argument is optional
6987 // FF Nightly sets third argument as `new.target`, but does not create `this` from it
6988 var NEW_TARGET_BUG = fails(function () {
6989 function F() { /* empty */ }
6990 return !(nativeConstruct(function () { /* empty */ }, [], F) instanceof F);
6992 var ARGS_BUG = !fails(function () {
6993 nativeConstruct(function () { /* empty */ });
6995 var FORCED$6 = NEW_TARGET_BUG || ARGS_BUG;
6997 _export({ target: 'Reflect', stat: true, forced: FORCED$6, sham: FORCED$6 }, {
6998 construct: function construct(Target, args /* , newTarget */) {
6999 aFunction$1(Target);
7001 var newTarget = arguments.length < 3 ? Target : aFunction$1(arguments[2]);
7002 if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget);
7003 if (Target == newTarget) {
7004 // w/o altered newTarget, optimization for 0-4 arguments
7005 switch (args.length) {
7006 case 0: return new Target();
7007 case 1: return new Target(args[0]);
7008 case 2: return new Target(args[0], args[1]);
7009 case 3: return new Target(args[0], args[1], args[2]);
7010 case 4: return new Target(args[0], args[1], args[2], args[3]);
7012 // w/o altered newTarget, lot of arguments case
7014 $args.push.apply($args, args);
7015 return new (functionBind.apply(Target, $args))();
7017 // with altered newTarget, not support built-in constructors
7018 var proto = newTarget.prototype;
7019 var instance = objectCreate(isObject(proto) ? proto : Object.prototype);
7020 var result = Function.apply.call(Target, instance, args);
7021 return isObject(result) ? result : instance;
7025 // `Reflect.get` method
7026 // https://tc39.github.io/ecma262/#sec-reflect.get
7027 function get$2(target, propertyKey /* , receiver */) {
7028 var receiver = arguments.length < 3 ? target : arguments[2];
7029 var descriptor, prototype;
7030 if (anObject(target) === receiver) return target[propertyKey];
7031 if (descriptor = objectGetOwnPropertyDescriptor.f(target, propertyKey)) return has(descriptor, 'value')
7033 : descriptor.get === undefined
7035 : descriptor.get.call(receiver);
7036 if (isObject(prototype = objectGetPrototypeOf(target))) return get$2(prototype, propertyKey, receiver);
7039 _export({ target: 'Reflect', stat: true }, {
7043 (function (factory) {
7047 function _classCallCheck(instance, Constructor) {
7048 if (!(instance instanceof Constructor)) {
7049 throw new TypeError("Cannot call a class as a function");
7053 function _defineProperties(target, props) {
7054 for (var i = 0; i < props.length; i++) {
7055 var descriptor = props[i];
7056 descriptor.enumerable = descriptor.enumerable || false;
7057 descriptor.configurable = true;
7058 if ("value" in descriptor) descriptor.writable = true;
7059 Object.defineProperty(target, descriptor.key, descriptor);
7063 function _createClass(Constructor, protoProps, staticProps) {
7064 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
7065 if (staticProps) _defineProperties(Constructor, staticProps);
7069 function _inherits(subClass, superClass) {
7070 if (typeof superClass !== "function" && superClass !== null) {
7071 throw new TypeError("Super expression must either be null or a function");
7074 subClass.prototype = Object.create(superClass && superClass.prototype, {
7081 if (superClass) _setPrototypeOf(subClass, superClass);
7084 function _getPrototypeOf(o) {
7085 _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
7086 return o.__proto__ || Object.getPrototypeOf(o);
7088 return _getPrototypeOf(o);
7091 function _setPrototypeOf(o, p) {
7092 _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
7097 return _setPrototypeOf(o, p);
7100 function _isNativeReflectConstruct() {
7101 if (typeof Reflect === "undefined" || !Reflect.construct) return false;
7102 if (Reflect.construct.sham) return false;
7103 if (typeof Proxy === "function") return true;
7106 Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
7113 function _assertThisInitialized(self) {
7114 if (self === void 0) {
7115 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
7121 function _possibleConstructorReturn(self, call) {
7122 if (call && (_typeof(call) === "object" || typeof call === "function")) {
7126 return _assertThisInitialized(self);
7129 function _createSuper(Derived) {
7130 var hasNativeReflectConstruct = _isNativeReflectConstruct();
7132 return function _createSuperInternal() {
7133 var Super = _getPrototypeOf(Derived),
7136 if (hasNativeReflectConstruct) {
7137 var NewTarget = _getPrototypeOf(this).constructor;
7139 result = Reflect.construct(Super, arguments, NewTarget);
7141 result = Super.apply(this, arguments);
7144 return _possibleConstructorReturn(this, result);
7148 function _superPropBase(object, property) {
7149 while (!Object.prototype.hasOwnProperty.call(object, property)) {
7150 object = _getPrototypeOf(object);
7151 if (object === null) break;
7157 function _get(target, property, receiver) {
7158 if (typeof Reflect !== "undefined" && Reflect.get) {
7161 _get = function _get(target, property, receiver) {
7162 var base = _superPropBase(target, property);
7165 var desc = Object.getOwnPropertyDescriptor(base, property);
7168 return desc.get.call(receiver);
7175 return _get(target, property, receiver || target);
7178 var Emitter = /*#__PURE__*/function () {
7179 function Emitter() {
7180 _classCallCheck(this, Emitter);
7182 Object.defineProperty(this, 'listeners', {
7189 _createClass(Emitter, [{
7190 key: "addEventListener",
7191 value: function addEventListener(type, callback) {
7192 if (!(type in this.listeners)) {
7193 this.listeners[type] = [];
7196 this.listeners[type].push(callback);
7199 key: "removeEventListener",
7200 value: function removeEventListener(type, callback) {
7201 if (!(type in this.listeners)) {
7205 var stack = this.listeners[type];
7207 for (var i = 0, l = stack.length; i < l; i++) {
7208 if (stack[i] === callback) {
7215 key: "dispatchEvent",
7216 value: function dispatchEvent(event) {
7219 if (!(event.type in this.listeners)) {
7223 var debounce = function debounce(callback) {
7224 setTimeout(function () {
7225 return callback.call(_this, event);
7229 var stack = this.listeners[event.type];
7231 for (var i = 0, l = stack.length; i < l; i++) {
7235 return !event.defaultPrevented;
7242 var AbortSignal = /*#__PURE__*/function (_Emitter) {
7243 _inherits(AbortSignal, _Emitter);
7245 var _super = _createSuper(AbortSignal);
7247 function AbortSignal() {
7250 _classCallCheck(this, AbortSignal);
7252 _this2 = _super.call(this); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
7253 // constructor has failed to run, then "this.listeners" will still be undefined and then we call
7254 // the parent constructor directly instead as a workaround. For general details, see babel bug:
7255 // https://github.com/babel/babel/issues/3041
7256 // This hack was added as a fix for the issue described here:
7257 // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
7259 if (!_this2.listeners) {
7260 Emitter.call(_assertThisInitialized(_this2));
7261 } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7262 // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
7265 Object.defineProperty(_assertThisInitialized(_this2), 'aborted', {
7270 Object.defineProperty(_assertThisInitialized(_this2), 'onabort', {
7278 _createClass(AbortSignal, [{
7280 value: function toString() {
7281 return '[object AbortSignal]';
7284 key: "dispatchEvent",
7285 value: function dispatchEvent(event) {
7286 if (event.type === 'abort') {
7287 this.aborted = true;
7289 if (typeof this.onabort === 'function') {
7290 this.onabort.call(this, event);
7294 _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event);
7301 var AbortController = /*#__PURE__*/function () {
7302 function AbortController() {
7303 _classCallCheck(this, AbortController); // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
7304 // we want Object.keys(new AbortController()) to be [] for compat with the native impl
7307 Object.defineProperty(this, 'signal', {
7308 value: new AbortSignal(),
7314 _createClass(AbortController, [{
7316 value: function abort() {
7320 event = new Event('abort');
7322 if (typeof document !== 'undefined') {
7323 if (!document.createEvent) {
7324 // For Internet Explorer 8:
7325 event = document.createEventObject();
7326 event.type = 'abort';
7328 // For Internet Explorer 11:
7329 event = document.createEvent('Event');
7330 event.initEvent('abort', false, false);
7333 // Fallback where document isn't available:
7342 this.signal.dispatchEvent(event);
7346 value: function toString() {
7347 return '[object AbortController]';
7351 return AbortController;
7354 if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
7355 // These are necessary to make sure that we get correct output for:
7356 // Object.prototype.toString.call(new AbortController())
7357 AbortController.prototype[Symbol.toStringTag] = 'AbortController';
7358 AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
7361 function polyfillNeeded(self) {
7362 if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7363 console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill');
7365 } // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7366 // defining window.Request, and this polyfill need to work on top of unfetch
7367 // so the below feature detection needs the !self.AbortController part.
7368 // The Request.prototype check is also needed because Safari versions 11.1.2
7369 // up to and including 12.1.x has a window.AbortController present but still
7370 // does NOT correctly implement abortable fetch:
7371 // https://bugs.webkit.org/show_bug.cgi?id=174980#c2
7374 return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController;
7377 * Note: the "fetch.Request" default value is available for fetch imported from
7378 * the "node-fetch" package and not in browsers. This is OK since browsers
7379 * will be importing umd-polyfill.js from that path "self" is passed the
7380 * decorator so the default value will not be used (because browsers that define
7381 * fetch also has Request). One quirky setup where self.fetch exists but
7382 * self.Request does not is when the "unfetch" minimal fetch polyfill is used
7383 * on top of IE11; for this case the browser will try to use the fetch.Request
7384 * default value which in turn will be undefined but then then "if (Request)"
7385 * will ensure that you get a patched fetch but still no Request (as expected).
7386 * @param {fetch, Request = fetch.Request}
7387 * @returns {fetch: abortableFetch, Request: AbortableRequest}
7391 function abortableFetchDecorator(patchTargets) {
7392 if ('function' === typeof patchTargets) {
7398 var _patchTargets = patchTargets,
7399 fetch = _patchTargets.fetch,
7400 _patchTargets$Request = _patchTargets.Request,
7401 NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request,
7402 NativeAbortController = _patchTargets.AbortController,
7403 _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
7404 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE;
7406 if (!polyfillNeeded({
7408 Request: NativeRequest,
7409 AbortController: NativeAbortController,
7410 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL
7418 var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without
7419 // defining window.Request, and this polyfill need to work on top of unfetch
7420 // hence we only patch it if it's available. Also we don't patch it if signal
7421 // is already available on the Request prototype because in this case support
7422 // is present and the patching below can cause a crash since it assigns to
7423 // request.signal which is technically a read-only property. This latter error
7424 // happens when you run the main5.js node-fetch example in the repo
7425 // "abortcontroller-polyfill-examples". The exact error is:
7426 // request.signal = init.signal;
7428 // TypeError: Cannot set property signal of #<Request> which has only a getter
7430 if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
7431 Request = function Request(input, init) {
7434 if (init && init.signal) {
7435 signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has
7436 // been installed because if we're running on top of a browser with a
7437 // working native AbortController (i.e. the polyfill was installed due to
7438 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7439 // fake AbortSignal to the native fetch will trigger:
7440 // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal.
7445 var request = new NativeRequest(input, init);
7448 Object.defineProperty(request, 'signal', {
7459 Request.prototype = NativeRequest.prototype;
7462 var realFetch = fetch;
7464 var abortableFetch = function abortableFetch(input, init) {
7465 var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined;
7471 abortError = new DOMException('Aborted', 'AbortError');
7473 // IE 11 does not support calling the DOMException constructor, use a
7474 // regular error object on it instead.
7475 abortError = new Error('Aborted');
7476 abortError.name = 'AbortError';
7477 } // Return early if already aborted, thus avoiding making an HTTP request
7480 if (signal.aborted) {
7481 return Promise.reject(abortError);
7482 } // Turn an event into a promise, reject it once `abort` is dispatched
7485 var cancellation = new Promise(function (_, reject) {
7486 signal.addEventListener('abort', function () {
7487 return reject(abortError);
7493 if (init && init.signal) {
7494 // Never pass .signal to the native implementation when the polyfill has
7495 // been installed because if we're running on top of a browser with a
7496 // working native AbortController (i.e. the polyfill was installed due to
7497 // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
7498 // fake AbortSignal to the native fetch will trigger:
7499 // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal.
7501 } // Return the fastest promise (don't need to wait for request to finish)
7504 return Promise.race([cancellation, realFetch(input, init)]);
7507 return realFetch(input, init);
7511 fetch: abortableFetch,
7517 if (!polyfillNeeded(self)) {
7522 console.warn('fetch() is not available, cannot install abortcontroller-polyfill');
7526 var _abortableFetch = abortableFetchDecorator(self),
7527 fetch = _abortableFetch.fetch,
7528 Request = _abortableFetch.Request;
7531 self.Request = Request;
7532 Object.defineProperty(self, 'AbortController', {
7536 value: AbortController
7538 Object.defineProperty(self, 'AbortSignal', {
7544 })(typeof self !== 'undefined' ? self : commonjsGlobal);
7547 function actionAddEntity(way) {
7548 return function (graph) {
7549 return graph.replace(way);
7553 var IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable');
7554 var MAX_SAFE_INTEGER$1 = 0x1FFFFFFFFFFFFF;
7555 var MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded';
7557 // We can't use this feature detection in V8 since it causes
7558 // deoptimization and serious performance degradation
7559 // https://github.com/zloirock/core-js/issues/679
7560 var IS_CONCAT_SPREADABLE_SUPPORT = engineV8Version >= 51 || !fails(function () {
7562 array[IS_CONCAT_SPREADABLE] = false;
7563 return array.concat()[0] !== array;
7566 var SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('concat');
7568 var isConcatSpreadable = function (O) {
7569 if (!isObject(O)) return false;
7570 var spreadable = O[IS_CONCAT_SPREADABLE];
7571 return spreadable !== undefined ? !!spreadable : isArray(O);
7574 var FORCED$7 = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT;
7576 // `Array.prototype.concat` method
7577 // https://tc39.github.io/ecma262/#sec-array.prototype.concat
7578 // with adding support of @@isConcatSpreadable and @@species
7579 _export({ target: 'Array', proto: true, forced: FORCED$7 }, {
7580 concat: function concat(arg) { // eslint-disable-line no-unused-vars
7581 var O = toObject(this);
7582 var A = arraySpeciesCreate(O, 0);
7584 var i, k, length, len, E;
7585 for (i = -1, length = arguments.length; i < length; i++) {
7586 E = i === -1 ? O : arguments[i];
7587 if (isConcatSpreadable(E)) {
7588 len = toLength(E.length);
7589 if (n + len > MAX_SAFE_INTEGER$1) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7590 for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]);
7592 if (n >= MAX_SAFE_INTEGER$1) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);
7593 createProperty(A, n++, E);
7601 // `Object.assign` method
7602 // https://tc39.github.io/ecma262/#sec-object.assign
7603 _export({ target: 'Object', stat: true, forced: Object.assign !== objectAssign }, {
7604 assign: objectAssign
7607 var $filter$1 = arrayIteration.filter;
7611 var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport('filter');
7613 var USES_TO_LENGTH$6 = arrayMethodUsesToLength('filter');
7615 // `Array.prototype.filter` method
7616 // https://tc39.github.io/ecma262/#sec-array.prototype.filter
7617 // with adding support of @@species
7618 _export({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 || !USES_TO_LENGTH$6 }, {
7619 filter: function filter(callbackfn /* , thisArg */) {
7620 return $filter$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
7624 var nativeReverse = [].reverse;
7625 var test$1 = [1, 2];
7627 // `Array.prototype.reverse` method
7628 // https://tc39.github.io/ecma262/#sec-array.prototype.reverse
7629 // fix for Safari 12.0 bug
7630 // https://bugs.webkit.org/show_bug.cgi?id=188794
7631 _export({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, {
7632 reverse: function reverse() {
7633 // eslint-disable-next-line no-self-assign
7634 if (isArray(this)) this.length = this.length;
7635 return nativeReverse.call(this);
7639 var FAILS_ON_PRIMITIVES$3 = fails(function () { objectKeys(1); });
7641 // `Object.keys` method
7642 // https://tc39.github.io/ecma262/#sec-object.keys
7643 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$3 }, {
7644 keys: function keys(it) {
7645 return objectKeys(toObject(it));
7649 var trim = stringTrim.trim;
7652 var $parseFloat = global_1.parseFloat;
7653 var FORCED$8 = 1 / $parseFloat(whitespaces + '-0') !== -Infinity;
7655 // `parseFloat` method
7656 // https://tc39.github.io/ecma262/#sec-parsefloat-string
7657 var numberParseFloat = FORCED$8 ? function parseFloat(string) {
7658 var trimmedString = trim(String(string));
7659 var result = $parseFloat(trimmedString);
7660 return result === 0 && trimmedString.charAt(0) == '-' ? -0 : result;
7663 // `parseFloat` method
7664 // https://tc39.github.io/ecma262/#sec-parsefloat-string
7665 _export({ global: true, forced: parseFloat != numberParseFloat }, {
7666 parseFloat: numberParseFloat
7670 Order the nodes of a way in reverse order and reverse any direction dependent tags
7671 other than `oneway`. (We assume that correcting a backwards oneway is the primary
7672 reason for reversing a way.)
7674 In addition, numeric-valued `incline` tags are negated.
7676 The JOSM implementation was used as a guide, but transformations that were of unclear benefit
7677 or adjusted tags that don't seem to be used in practice were omitted.
7680 http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
7681 http://wiki.openstreetmap.org/wiki/Key:direction#Steps
7682 http://wiki.openstreetmap.org/wiki/Key:incline
7683 http://wiki.openstreetmap.org/wiki/Route#Members
7684 http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
7685 http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
7686 http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
7688 function actionReverse(entityID, options) {
7689 var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/;
7690 var numeric = /^([+\-]?)(?=[\d.])/;
7691 var directionKey = /direction$/;
7692 var turn_lanes = /^turn:lanes:?/;
7693 var keyReplacements = [[/:right$/, ':left'], [/:left$/, ':right'], [/:forward$/, ':backward'], [/:backward$/, ':forward'], [/:right:/, ':left:'], [/:left:/, ':right:'], [/:forward:/, ':backward:'], [/:backward:/, ':forward:']];
7694 var valueReplacements = {
7699 forward: 'backward',
7700 backward: 'forward',
7701 forwards: 'backward',
7702 backwards: 'forward'
7704 var roleReplacements = {
7705 forward: 'backward',
7706 backward: 'forward',
7707 forwards: 'backward',
7708 backwards: 'forward'
7710 var onewayReplacements = {
7715 var compassReplacements = {
7734 function reverseKey(key) {
7735 for (var i = 0; i < keyReplacements.length; ++i) {
7736 var replacement = keyReplacements[i];
7738 if (replacement[0].test(key)) {
7739 return key.replace(replacement[0], replacement[1]);
7746 function reverseValue(key, value, includeAbsolute) {
7747 if (ignoreKey.test(key)) return value; // Turn lanes are left/right to key (not way) direction - #5674
7749 if (turn_lanes.test(key)) {
7751 } else if (key === 'incline' && numeric.test(value)) {
7752 return value.replace(numeric, function (_, sign) {
7753 return sign === '-' ? '' : '-';
7755 } else if (options && options.reverseOneway && key === 'oneway') {
7756 return onewayReplacements[value] || value;
7757 } else if (includeAbsolute && directionKey.test(key)) {
7758 if (compassReplacements[value]) return compassReplacements[value];
7759 var degrees = parseFloat(value);
7761 if (typeof degrees === 'number' && !isNaN(degrees)) {
7762 if (degrees < 180) {
7768 return degrees.toString();
7772 return valueReplacements[value] || value;
7773 } // Reverse the direction of tags attached to the nodes - #3076
7776 function reverseNodeTags(graph, nodeIDs) {
7777 for (var i = 0; i < nodeIDs.length; i++) {
7778 var node = graph.hasEntity(nodeIDs[i]);
7779 if (!node || !Object.keys(node.tags).length) continue;
7782 for (var key in node.tags) {
7783 tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID);
7786 graph = graph.replace(node.update({
7794 function reverseWay(graph, way) {
7795 var nodes = way.nodes.slice().reverse();
7799 for (var key in way.tags) {
7800 tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
7803 graph.parentRelations(way).forEach(function (relation) {
7804 relation.members.forEach(function (member, index) {
7805 if (member.id === way.id && (role = roleReplacements[member.role])) {
7806 relation = relation.updateMember({
7809 graph = graph.replace(relation);
7812 }); // Reverse any associated directions on nodes on the way and then replace
7813 // the way itself with the reversed node ids and updated way tags
7815 return reverseNodeTags(graph, nodes).replace(way.update({
7821 var action = function action(graph) {
7822 var entity = graph.entity(entityID);
7824 if (entity.type === 'way') {
7825 return reverseWay(graph, entity);
7828 return reverseNodeTags(graph, [entityID]);
7831 action.disabled = function (graph) {
7832 var entity = graph.hasEntity(entityID);
7833 if (!entity || entity.type === 'way') return false;
7835 for (var key in entity.tags) {
7836 var value = entity.tags[key];
7838 if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) {
7843 return 'nondirectional_node';
7846 action.entityID = function () {
7853 function osmIsInterestingTag(key) {
7854 return key !== 'attribution' && key !== 'created_by' && key !== 'source' && key !== 'odbl' && key.indexOf('source:') !== 0 && key.indexOf('source_ref') !== 0 && // purposely exclude colon
7855 key.indexOf('tiger:') !== 0;
7857 var osmAreaKeys = {};
7858 function osmSetAreaKeys(value) {
7859 osmAreaKeys = value;
7860 } // returns an object with the tag from `tags` that implies an area geometry, if any
7862 function osmTagSuggestingArea(tags) {
7863 if (tags.area === 'yes') return {
7866 if (tags.area === 'no') return null; // `highway` and `railway` are typically linear features, but there
7867 // are a few exceptions that should be treated as areas, even in the
7868 // absence of a proper `area=yes` or `areaKeys` tag.. see #4194
7883 var returnTags = {};
7885 for (var key in tags) {
7886 if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
7887 returnTags[key] = tags[key];
7891 if (key in lineKeys && tags[key] in lineKeys[key]) {
7892 returnTags[key] = tags[key];
7898 } // Tags that indicate a node can be a standalone point
7899 // e.g. { amenity: { bar: true, parking: true, ... } ... }
7901 var osmPointTags = {};
7902 function osmSetPointTags(value) {
7903 osmPointTags = value;
7904 } // Tags that indicate a node can be part of a way
7905 // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... }
7907 var osmVertexTags = {};
7908 function osmSetVertexTags(value) {
7909 osmVertexTags = value;
7911 function osmNodeGeometriesForTags(nodeTags) {
7912 var geometries = {};
7914 for (var key in nodeTags) {
7915 if (osmPointTags[key] && (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) {
7916 geometries.point = true;
7919 if (osmVertexTags[key] && (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) {
7920 geometries.vertex = true;
7921 } // break early if both are already supported
7924 if (geometries.point && geometries.vertex) break;
7929 var osmOneWayTags = {
7934 'magic_carpet': true,
7949 'goods_conveyor': true,
7950 'piste:halfpipe': true
7964 'tidal_channel': true
7966 }; // solid and smooth surfaces akin to the assumed default road surface in OSM
7968 var osmPavedTags = {
7973 'concrete:lanes': true,
7974 'concrete:plates': true
7979 }; // solid, if somewhat uncommon surfaces with a high range of smoothness
7981 var osmSemipavedTags = {
7983 'cobblestone': true,
7984 'cobblestone:flattened': true,
7985 'unhewn_cobblestone': true,
7987 'paving_stones': true,
7992 var osmRightSideIsInsideTags = {
7995 'coastline': 'coastline'
7998 'retaining_wall': true,
8009 }; // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network
8010 // (does not include `raceway`)
8012 var osmRoutableHighwayTagValues = {
8019 motorway_link: true,
8022 secondary_link: true,
8023 tertiary_link: true,
8028 living_street: true,
8037 }; // "highway" tag values that generally do not allow motor vehicles
8039 var osmPathHighwayTagValues = {
8047 }; // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned')
8049 var osmRailwayTrackTagValues = {
8060 }; // "waterway" tag values for line features representing water flow
8062 var osmFlowingWaterwayTagValues = {
8072 var trim$1 = stringTrim.trim;
8075 var $parseInt = global_1.parseInt;
8076 var hex$1 = /^[+-]?0[Xx]/;
8077 var FORCED$9 = $parseInt(whitespaces + '08') !== 8 || $parseInt(whitespaces + '0x16') !== 22;
8079 // `parseInt` method
8080 // https://tc39.github.io/ecma262/#sec-parseint-string-radix
8081 var numberParseInt = FORCED$9 ? function parseInt(string, radix) {
8082 var S = trim$1(String(string));
8083 return $parseInt(S, (radix >>> 0) || (hex$1.test(S) ? 16 : 10));
8086 // `parseInt` method
8087 // https://tc39.github.io/ecma262/#sec-parseint-string-radix
8088 _export({ global: true, forced: parseInt != numberParseInt }, {
8089 parseInt: numberParseInt
8092 var freezing = !fails(function () {
8093 return Object.isExtensible(Object.preventExtensions({}));
8096 var internalMetadata = createCommonjsModule(function (module) {
8097 var defineProperty = objectDefineProperty.f;
8101 var METADATA = uid('meta');
8104 var isExtensible = Object.isExtensible || function () {
8108 var setMetadata = function (it) {
8109 defineProperty(it, METADATA, { value: {
8110 objectID: 'O' + ++id, // object ID
8111 weakData: {} // weak collections IDs
8115 var fastKey = function (it, create) {
8116 // return a primitive with prefix
8117 if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
8118 if (!has(it, METADATA)) {
8119 // can't set metadata to uncaught frozen object
8120 if (!isExtensible(it)) return 'F';
8121 // not necessary to add metadata
8122 if (!create) return 'E';
8123 // add missing metadata
8126 } return it[METADATA].objectID;
8129 var getWeakData = function (it, create) {
8130 if (!has(it, METADATA)) {
8131 // can't set metadata to uncaught frozen object
8132 if (!isExtensible(it)) return true;
8133 // not necessary to add metadata
8134 if (!create) return false;
8135 // add missing metadata
8137 // return the store of weak collections IDs
8138 } return it[METADATA].weakData;
8141 // add metadata on freeze-family methods calling
8142 var onFreeze = function (it) {
8143 if (freezing && meta.REQUIRED && isExtensible(it) && !has(it, METADATA)) setMetadata(it);
8147 var meta = module.exports = {
8150 getWeakData: getWeakData,
8154 hiddenKeys[METADATA] = true;
8157 var collection = function (CONSTRUCTOR_NAME, wrapper, common) {
8158 var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1;
8159 var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1;
8160 var ADDER = IS_MAP ? 'set' : 'add';
8161 var NativeConstructor = global_1[CONSTRUCTOR_NAME];
8162 var NativePrototype = NativeConstructor && NativeConstructor.prototype;
8163 var Constructor = NativeConstructor;
8166 var fixMethod = function (KEY) {
8167 var nativeMethod = NativePrototype[KEY];
8168 redefine(NativePrototype, KEY,
8169 KEY == 'add' ? function add(value) {
8170 nativeMethod.call(this, value === 0 ? 0 : value);
8172 } : KEY == 'delete' ? function (key) {
8173 return IS_WEAK && !isObject(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8174 } : KEY == 'get' ? function get(key) {
8175 return IS_WEAK && !isObject(key) ? undefined : nativeMethod.call(this, key === 0 ? 0 : key);
8176 } : KEY == 'has' ? function has(key) {
8177 return IS_WEAK && !isObject(key) ? false : nativeMethod.call(this, key === 0 ? 0 : key);
8178 } : function set(key, value) {
8179 nativeMethod.call(this, key === 0 ? 0 : key, value);
8185 // eslint-disable-next-line max-len
8186 if (isForced_1(CONSTRUCTOR_NAME, typeof NativeConstructor != 'function' || !(IS_WEAK || NativePrototype.forEach && !fails(function () {
8187 new NativeConstructor().entries().next();
8189 // create collection constructor
8190 Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER);
8191 internalMetadata.REQUIRED = true;
8192 } else if (isForced_1(CONSTRUCTOR_NAME, true)) {
8193 var instance = new Constructor();
8194 // early implementations not supports chaining
8195 var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance;
8196 // V8 ~ Chromium 40- weak-collections throws on primitives, but should return false
8197 var THROWS_ON_PRIMITIVES = fails(function () { instance.has(1); });
8198 // most early implementations doesn't supports iterables, most modern - not close it correctly
8199 // eslint-disable-next-line no-new
8200 var ACCEPT_ITERABLES = checkCorrectnessOfIteration(function (iterable) { new NativeConstructor(iterable); });
8201 // for early implementations -0 and +0 not the same
8202 var BUGGY_ZERO = !IS_WEAK && fails(function () {
8203 // V8 ~ Chromium 42- fails only with 5+ elements
8204 var $instance = new NativeConstructor();
8206 while (index--) $instance[ADDER](index, index);
8207 return !$instance.has(-0);
8210 if (!ACCEPT_ITERABLES) {
8211 Constructor = wrapper(function (dummy, iterable) {
8212 anInstance(dummy, Constructor, CONSTRUCTOR_NAME);
8213 var that = inheritIfRequired(new NativeConstructor(), dummy, Constructor);
8214 if (iterable != undefined) iterate_1(iterable, that[ADDER], that, IS_MAP);
8217 Constructor.prototype = NativePrototype;
8218 NativePrototype.constructor = Constructor;
8221 if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) {
8222 fixMethod('delete');
8224 IS_MAP && fixMethod('get');
8227 if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER);
8229 // weak collections should not contains .clear method
8230 if (IS_WEAK && NativePrototype.clear) delete NativePrototype.clear;
8233 exported[CONSTRUCTOR_NAME] = Constructor;
8234 _export({ global: true, forced: Constructor != NativeConstructor }, exported);
8236 setToStringTag(Constructor, CONSTRUCTOR_NAME);
8238 if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP);
8243 var defineProperty$8 = objectDefineProperty.f;
8252 var fastKey = internalMetadata.fastKey;
8255 var setInternalState$7 = internalState.set;
8256 var internalStateGetterFor = internalState.getterFor;
8258 var collectionStrong = {
8259 getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) {
8260 var C = wrapper(function (that, iterable) {
8261 anInstance(that, C, CONSTRUCTOR_NAME);
8262 setInternalState$7(that, {
8263 type: CONSTRUCTOR_NAME,
8264 index: objectCreate(null),
8269 if (!descriptors) that.size = 0;
8270 if (iterable != undefined) iterate_1(iterable, that[ADDER], that, IS_MAP);
8273 var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME);
8275 var define = function (that, key, value) {
8276 var state = getInternalState(that);
8277 var entry = getEntry(that, key);
8278 var previous, index;
8279 // change existing entry
8281 entry.value = value;
8284 state.last = entry = {
8285 index: index = fastKey(key, true),
8288 previous: previous = state.last,
8292 if (!state.first) state.first = entry;
8293 if (previous) previous.next = entry;
8294 if (descriptors) state.size++;
8297 if (index !== 'F') state.index[index] = entry;
8301 var getEntry = function (that, key) {
8302 var state = getInternalState(that);
8304 var index = fastKey(key);
8306 if (index !== 'F') return state.index[index];
8307 // frozen object case
8308 for (entry = state.first; entry; entry = entry.next) {
8309 if (entry.key == key) return entry;
8313 redefineAll(C.prototype, {
8314 // 23.1.3.1 Map.prototype.clear()
8315 // 23.2.3.2 Set.prototype.clear()
8316 clear: function clear() {
8318 var state = getInternalState(that);
8319 var data = state.index;
8320 var entry = state.first;
8322 entry.removed = true;
8323 if (entry.previous) entry.previous = entry.previous.next = undefined;
8324 delete data[entry.index];
8327 state.first = state.last = undefined;
8328 if (descriptors) state.size = 0;
8331 // 23.1.3.3 Map.prototype.delete(key)
8332 // 23.2.3.4 Set.prototype.delete(value)
8333 'delete': function (key) {
8335 var state = getInternalState(that);
8336 var entry = getEntry(that, key);
8338 var next = entry.next;
8339 var prev = entry.previous;
8340 delete state.index[entry.index];
8341 entry.removed = true;
8342 if (prev) prev.next = next;
8343 if (next) next.previous = prev;
8344 if (state.first == entry) state.first = next;
8345 if (state.last == entry) state.last = prev;
8346 if (descriptors) state.size--;
8350 // 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined)
8351 // 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined)
8352 forEach: function forEach(callbackfn /* , that = undefined */) {
8353 var state = getInternalState(this);
8354 var boundFunction = functionBindContext(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3);
8356 while (entry = entry ? entry.next : state.first) {
8357 boundFunction(entry.value, entry.key, this);
8358 // revert to the last existing entry
8359 while (entry && entry.removed) entry = entry.previous;
8362 // 23.1.3.7 Map.prototype.has(key)
8363 // 23.2.3.7 Set.prototype.has(value)
8364 has: function has(key) {
8365 return !!getEntry(this, key);
8369 redefineAll(C.prototype, IS_MAP ? {
8370 // 23.1.3.6 Map.prototype.get(key)
8371 get: function get(key) {
8372 var entry = getEntry(this, key);
8373 return entry && entry.value;
8375 // 23.1.3.9 Map.prototype.set(key, value)
8376 set: function set(key, value) {
8377 return define(this, key === 0 ? 0 : key, value);
8380 // 23.2.3.1 Set.prototype.add(value)
8381 add: function add(value) {
8382 return define(this, value = value === 0 ? 0 : value, value);
8385 if (descriptors) defineProperty$8(C.prototype, 'size', {
8387 return getInternalState(this).size;
8392 setStrong: function (C, CONSTRUCTOR_NAME, IS_MAP) {
8393 var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator';
8394 var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME);
8395 var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME);
8396 // add .keys, .values, .entries, [@@iterator]
8397 // 23.1.3.4, 23.1.3.8, 23.1.3.11, 23.1.3.12, 23.2.3.5, 23.2.3.8, 23.2.3.10, 23.2.3.11
8398 defineIterator(C, CONSTRUCTOR_NAME, function (iterated, kind) {
8399 setInternalState$7(this, {
8400 type: ITERATOR_NAME,
8402 state: getInternalCollectionState(iterated),
8407 var state = getInternalIteratorState(this);
8408 var kind = state.kind;
8409 var entry = state.last;
8410 // revert to the last existing entry
8411 while (entry && entry.removed) entry = entry.previous;
8413 if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) {
8414 // or finish the iteration
8415 state.target = undefined;
8416 return { value: undefined, done: true };
8418 // return step by kind
8419 if (kind == 'keys') return { value: entry.key, done: false };
8420 if (kind == 'values') return { value: entry.value, done: false };
8421 return { value: [entry.key, entry.value], done: false };
8422 }, IS_MAP ? 'entries' : 'values', !IS_MAP, true);
8424 // add [@@species], 23.1.2.2, 23.2.2.2
8425 setSpecies(CONSTRUCTOR_NAME);
8429 // `Set` constructor
8430 // https://tc39.github.io/ecma262/#sec-set-objects
8431 var es_set = collection('Set', function (init) {
8432 return function Set() { return init(this, arguments.length ? arguments[0] : undefined); };
8433 }, collectionStrong);
8435 function d3_ascending (a, b) {
8436 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
8439 function d3_bisector (f) {
8443 if (f.length === 1) {
8444 delta = function delta(d, x) {
8448 compare = ascendingComparator(f);
8451 function left(a, x, lo, hi) {
8452 if (lo == null) lo = 0;
8453 if (hi == null) hi = a.length;
8456 var mid = lo + hi >>> 1;
8457 if (compare(a[mid], x) < 0) lo = mid + 1;else hi = mid;
8463 function right(a, x, lo, hi) {
8464 if (lo == null) lo = 0;
8465 if (hi == null) hi = a.length;
8468 var mid = lo + hi >>> 1;
8469 if (compare(a[mid], x) > 0) hi = mid;else lo = mid + 1;
8475 function center(a, x, lo, hi) {
8476 if (lo == null) lo = 0;
8477 if (hi == null) hi = a.length;
8478 var i = left(a, x, lo, hi - 1);
8479 return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i;
8489 function ascendingComparator(f) {
8490 return function (d, x) {
8491 return d3_ascending(f(d), x);
8495 // `Symbol.asyncIterator` well-known symbol
8496 // https://tc39.github.io/ecma262/#sec-symbol.asynciterator
8497 defineWellKnownSymbol('asyncIterator');
8499 var runtime_1 = createCommonjsModule(function (module) {
8501 * Copyright (c) 2014-present, Facebook, Inc.
8503 * This source code is licensed under the MIT license found in the
8504 * LICENSE file in the root directory of this source tree.
8506 var runtime = function (exports) {
8508 var Op = Object.prototype;
8509 var hasOwn = Op.hasOwnProperty;
8510 var undefined$1; // More compressible than void 0.
8512 var $Symbol = typeof Symbol === "function" ? Symbol : {};
8513 var iteratorSymbol = $Symbol.iterator || "@@iterator";
8514 var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
8515 var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
8517 function define(obj, key, value) {
8518 Object.defineProperty(obj, key, {
8528 // IE 8 has a broken Object.defineProperty that only works on DOM objects.
8531 define = function define(obj, key, value) {
8532 return obj[key] = value;
8536 function wrap(innerFn, outerFn, self, tryLocsList) {
8537 // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
8538 var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
8539 var generator = Object.create(protoGenerator.prototype);
8540 var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next,
8541 // .throw, and .return methods.
8543 generator._invoke = makeInvokeMethod(innerFn, self, context);
8547 exports.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion
8548 // record like context.tryEntries[i].completion. This interface could
8549 // have been (and was previously) designed to take a closure to be
8550 // invoked without arguments, but in all the cases we care about we
8551 // already have an existing method we want to call, so there's no need
8552 // to create a new function object. We can even get away with assuming
8553 // the method takes exactly one argument, since that happens to be true
8554 // in every case, so we don't have to touch the arguments object. The
8555 // only additional allocation required is the completion record, which
8556 // has a stable shape and so hopefully should be cheap to allocate.
8558 function tryCatch(fn, obj, arg) {
8562 arg: fn.call(obj, arg)
8572 var GenStateSuspendedStart = "suspendedStart";
8573 var GenStateSuspendedYield = "suspendedYield";
8574 var GenStateExecuting = "executing";
8575 var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as
8576 // breaking out of the dispatch switch statement.
8578 var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and
8579 // .constructor.prototype properties for functions that return Generator
8580 // objects. For full spec compliance, you may wish to configure your
8581 // minifier not to mangle the names of these two functions.
8583 function Generator() {}
8585 function GeneratorFunction() {}
8587 function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that
8588 // don't natively support it.
8591 var IteratorPrototype = {};
8593 IteratorPrototype[iteratorSymbol] = function () {
8597 var getProto = Object.getPrototypeOf;
8598 var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
8600 if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
8601 // This environment has a native %IteratorPrototype%; use it instead
8603 IteratorPrototype = NativeIteratorPrototype;
8606 var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
8607 GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
8608 GeneratorFunctionPrototype.constructor = GeneratorFunction;
8609 GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"); // Helper for defining the .next, .throw, and .return methods of the
8610 // Iterator interface in terms of a single ._invoke method.
8612 function defineIteratorMethods(prototype) {
8613 ["next", "throw", "return"].forEach(function (method) {
8614 define(prototype, method, function (arg) {
8615 return this._invoke(method, arg);
8620 exports.isGeneratorFunction = function (genFun) {
8621 var ctor = typeof genFun === "function" && genFun.constructor;
8622 return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can
8623 // do is to check its .name property.
8624 (ctor.displayName || ctor.name) === "GeneratorFunction" : false;
8627 exports.mark = function (genFun) {
8628 if (Object.setPrototypeOf) {
8629 Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
8631 genFun.__proto__ = GeneratorFunctionPrototype;
8632 define(genFun, toStringTagSymbol, "GeneratorFunction");
8635 genFun.prototype = Object.create(Gp);
8637 }; // Within the body of any async function, `await x` is transformed to
8638 // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
8639 // `hasOwn.call(value, "__await")` to determine if the yielded value is
8640 // meant to be awaited.
8643 exports.awrap = function (arg) {
8649 function AsyncIterator(generator, PromiseImpl) {
8650 function invoke(method, arg, resolve, reject) {
8651 var record = tryCatch(generator[method], generator, arg);
8653 if (record.type === "throw") {
8656 var result = record.arg;
8657 var value = result.value;
8659 if (value && _typeof(value) === "object" && hasOwn.call(value, "__await")) {
8660 return PromiseImpl.resolve(value.__await).then(function (value) {
8661 invoke("next", value, resolve, reject);
8663 invoke("throw", err, resolve, reject);
8667 return PromiseImpl.resolve(value).then(function (unwrapped) {
8668 // When a yielded Promise is resolved, its final value becomes
8669 // the .value of the Promise<{value,done}> result for the
8670 // current iteration.
8671 result.value = unwrapped;
8673 }, function (error) {
8674 // If a rejected Promise was yielded, throw the rejection back
8675 // into the async generator function so it can be handled there.
8676 return invoke("throw", error, resolve, reject);
8681 var previousPromise;
8683 function enqueue(method, arg) {
8684 function callInvokeWithMethodAndArg() {
8685 return new PromiseImpl(function (resolve, reject) {
8686 invoke(method, arg, resolve, reject);
8690 return previousPromise = // If enqueue has been called before, then we want to wait until
8691 // all previous Promises have been resolved before calling invoke,
8692 // so that results are always delivered in the correct order. If
8693 // enqueue has not been called before, then it is important to
8694 // call invoke immediately, without waiting on a callback to fire,
8695 // so that the async generator function has the opportunity to do
8696 // any necessary setup in a predictable way. This predictability
8697 // is why the Promise constructor synchronously invokes its
8698 // executor callback, and why async functions synchronously
8699 // execute code before the first await. Since we implement simple
8700 // async functions in terms of async generators, it is especially
8701 // important to get this right, even though it requires care.
8702 previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later
8703 // invocations of the iterator.
8704 callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
8705 } // Define the unified helper method that is used to implement .next,
8706 // .throw, and .return (see defineIteratorMethods).
8709 this._invoke = enqueue;
8712 defineIteratorMethods(AsyncIterator.prototype);
8714 AsyncIterator.prototype[asyncIteratorSymbol] = function () {
8718 exports.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of
8719 // AsyncIterator objects; they just return a Promise for the value of
8720 // the final result produced by the iterator.
8722 exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
8723 if (PromiseImpl === void 0) PromiseImpl = Promise;
8724 var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);
8725 return exports.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator.
8726 : iter.next().then(function (result) {
8727 return result.done ? result.value : iter.next();
8731 function makeInvokeMethod(innerFn, self, context) {
8732 var state = GenStateSuspendedStart;
8733 return function invoke(method, arg) {
8734 if (state === GenStateExecuting) {
8735 throw new Error("Generator is already running");
8738 if (state === GenStateCompleted) {
8739 if (method === "throw") {
8741 } // Be forgiving, per 25.3.3.3.3 of the spec:
8742 // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
8745 return doneResult();
8748 context.method = method;
8752 var delegate = context.delegate;
8755 var delegateResult = maybeInvokeDelegate(delegate, context);
8757 if (delegateResult) {
8758 if (delegateResult === ContinueSentinel) continue;
8759 return delegateResult;
8763 if (context.method === "next") {
8764 // Setting context._sent for legacy support of Babel's
8765 // function.sent implementation.
8766 context.sent = context._sent = context.arg;
8767 } else if (context.method === "throw") {
8768 if (state === GenStateSuspendedStart) {
8769 state = GenStateCompleted;
8773 context.dispatchException(context.arg);
8774 } else if (context.method === "return") {
8775 context.abrupt("return", context.arg);
8778 state = GenStateExecuting;
8779 var record = tryCatch(innerFn, self, context);
8781 if (record.type === "normal") {
8782 // If an exception is thrown from innerFn, we leave state ===
8783 // GenStateExecuting and loop back for another invocation.
8784 state = context.done ? GenStateCompleted : GenStateSuspendedYield;
8786 if (record.arg === ContinueSentinel) {
8794 } else if (record.type === "throw") {
8795 state = GenStateCompleted; // Dispatch the exception by looping back around to the
8796 // context.dispatchException(context.arg) call above.
8798 context.method = "throw";
8799 context.arg = record.arg;
8803 } // Call delegate.iterator[context.method](context.arg) and handle the
8804 // result, either by returning a { value, done } result from the
8805 // delegate iterator, or by modifying context.method and context.arg,
8806 // setting context.delegate to null, and returning the ContinueSentinel.
8809 function maybeInvokeDelegate(delegate, context) {
8810 var method = delegate.iterator[context.method];
8812 if (method === undefined$1) {
8813 // A .throw or .return when the delegate iterator has no .throw
8814 // method always terminates the yield* loop.
8815 context.delegate = null;
8817 if (context.method === "throw") {
8818 // Note: ["return"] must be used for ES3 parsing compatibility.
8819 if (delegate.iterator["return"]) {
8820 // If the delegate iterator has a return method, give it a
8821 // chance to clean up.
8822 context.method = "return";
8823 context.arg = undefined$1;
8824 maybeInvokeDelegate(delegate, context);
8826 if (context.method === "throw") {
8827 // If maybeInvokeDelegate(context) changed context.method from
8828 // "return" to "throw", let that override the TypeError below.
8829 return ContinueSentinel;
8833 context.method = "throw";
8834 context.arg = new TypeError("The iterator does not provide a 'throw' method");
8837 return ContinueSentinel;
8840 var record = tryCatch(method, delegate.iterator, context.arg);
8842 if (record.type === "throw") {
8843 context.method = "throw";
8844 context.arg = record.arg;
8845 context.delegate = null;
8846 return ContinueSentinel;
8849 var info = record.arg;
8852 context.method = "throw";
8853 context.arg = new TypeError("iterator result is not an object");
8854 context.delegate = null;
8855 return ContinueSentinel;
8859 // Assign the result of the finished delegate to the temporary
8860 // variable specified by delegate.resultName (see delegateYield).
8861 context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield).
8863 context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the
8864 // exception, let the outer generator proceed normally. If
8865 // context.method was "next", forget context.arg since it has been
8866 // "consumed" by the delegate iterator. If context.method was
8867 // "return", allow the original .return call to continue in the
8870 if (context.method !== "return") {
8871 context.method = "next";
8872 context.arg = undefined$1;
8875 // Re-yield the result returned by the delegate method.
8877 } // The delegate iterator is finished, so forget it and continue with
8878 // the outer generator.
8881 context.delegate = null;
8882 return ContinueSentinel;
8883 } // Define Generator.prototype.{next,throw,return} in terms of the
8884 // unified ._invoke helper method.
8887 defineIteratorMethods(Gp);
8888 define(Gp, toStringTagSymbol, "Generator"); // A Generator should always return itself as the iterator object when the
8889 // @@iterator function is called on it. Some browsers' implementations of the
8890 // iterator prototype chain incorrectly implement this, causing the Generator
8891 // object to not be returned from this call. This ensures that doesn't happen.
8892 // See https://github.com/facebook/regenerator/issues/274 for more details.
8894 Gp[iteratorSymbol] = function () {
8898 Gp.toString = function () {
8899 return "[object Generator]";
8902 function pushTryEntry(locs) {
8908 entry.catchLoc = locs[1];
8912 entry.finallyLoc = locs[2];
8913 entry.afterLoc = locs[3];
8916 this.tryEntries.push(entry);
8919 function resetTryEntry(entry) {
8920 var record = entry.completion || {};
8921 record.type = "normal";
8923 entry.completion = record;
8926 function Context(tryLocsList) {
8927 // The root entry object (effectively a try statement without a catch
8928 // or a finally block) gives us a place to store values thrown from
8929 // locations where there is no enclosing try statement.
8930 this.tryEntries = [{
8933 tryLocsList.forEach(pushTryEntry, this);
8937 exports.keys = function (object) {
8940 for (var key in object) {
8944 keys.reverse(); // Rather than returning an object with a next method, we keep
8945 // things simple and return the next function itself.
8947 return function next() {
8948 while (keys.length) {
8949 var key = keys.pop();
8951 if (key in object) {
8956 } // To avoid creating an additional object, we just hang the .value
8957 // and .done properties off the next function object itself. This
8958 // also ensures that the minifier will not anonymize the function.
8966 function values(iterable) {
8968 var iteratorMethod = iterable[iteratorSymbol];
8970 if (iteratorMethod) {
8971 return iteratorMethod.call(iterable);
8974 if (typeof iterable.next === "function") {
8978 if (!isNaN(iterable.length)) {
8980 next = function next() {
8981 while (++i < iterable.length) {
8982 if (hasOwn.call(iterable, i)) {
8983 next.value = iterable[i];
8989 next.value = undefined$1;
8994 return next.next = next;
8996 } // Return an iterator with no values.
9004 exports.values = values;
9006 function doneResult() {
9013 Context.prototype = {
9014 constructor: Context,
9015 reset: function reset(skipTempReset) {
9017 this.next = 0; // Resetting context._sent for legacy support of Babel's
9018 // function.sent implementation.
9020 this.sent = this._sent = undefined$1;
9022 this.delegate = null;
9023 this.method = "next";
9024 this.arg = undefined$1;
9025 this.tryEntries.forEach(resetTryEntry);
9027 if (!skipTempReset) {
9028 for (var name in this) {
9029 // Not sure about the optimal order of these conditions:
9030 if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) {
9031 this[name] = undefined$1;
9036 stop: function stop() {
9038 var rootEntry = this.tryEntries[0];
9039 var rootRecord = rootEntry.completion;
9041 if (rootRecord.type === "throw") {
9042 throw rootRecord.arg;
9047 dispatchException: function dispatchException(exception) {
9054 function handle(loc, caught) {
9055 record.type = "throw";
9056 record.arg = exception;
9060 // If the dispatched exception was caught by a catch block,
9061 // then let that catch block handle the exception normally.
9062 context.method = "next";
9063 context.arg = undefined$1;
9069 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9070 var entry = this.tryEntries[i];
9071 var record = entry.completion;
9073 if (entry.tryLoc === "root") {
9074 // Exception thrown outside of any try block that could handle
9075 // it, so set the completion value of the entire function to
9076 // throw the exception.
9077 return handle("end");
9080 if (entry.tryLoc <= this.prev) {
9081 var hasCatch = hasOwn.call(entry, "catchLoc");
9082 var hasFinally = hasOwn.call(entry, "finallyLoc");
9084 if (hasCatch && hasFinally) {
9085 if (this.prev < entry.catchLoc) {
9086 return handle(entry.catchLoc, true);
9087 } else if (this.prev < entry.finallyLoc) {
9088 return handle(entry.finallyLoc);
9090 } else if (hasCatch) {
9091 if (this.prev < entry.catchLoc) {
9092 return handle(entry.catchLoc, true);
9094 } else if (hasFinally) {
9095 if (this.prev < entry.finallyLoc) {
9096 return handle(entry.finallyLoc);
9099 throw new Error("try statement without catch or finally");
9104 abrupt: function abrupt(type, arg) {
9105 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9106 var entry = this.tryEntries[i];
9108 if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
9109 var finallyEntry = entry;
9114 if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) {
9115 // Ignore the finally entry if control is not jumping to a
9116 // location outside the try/catch block.
9117 finallyEntry = null;
9120 var record = finallyEntry ? finallyEntry.completion : {};
9125 this.method = "next";
9126 this.next = finallyEntry.finallyLoc;
9127 return ContinueSentinel;
9130 return this.complete(record);
9132 complete: function complete(record, afterLoc) {
9133 if (record.type === "throw") {
9137 if (record.type === "break" || record.type === "continue") {
9138 this.next = record.arg;
9139 } else if (record.type === "return") {
9140 this.rval = this.arg = record.arg;
9141 this.method = "return";
9143 } else if (record.type === "normal" && afterLoc) {
9144 this.next = afterLoc;
9147 return ContinueSentinel;
9149 finish: function finish(finallyLoc) {
9150 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9151 var entry = this.tryEntries[i];
9153 if (entry.finallyLoc === finallyLoc) {
9154 this.complete(entry.completion, entry.afterLoc);
9155 resetTryEntry(entry);
9156 return ContinueSentinel;
9160 "catch": function _catch(tryLoc) {
9161 for (var i = this.tryEntries.length - 1; i >= 0; --i) {
9162 var entry = this.tryEntries[i];
9164 if (entry.tryLoc === tryLoc) {
9165 var record = entry.completion;
9167 if (record.type === "throw") {
9168 var thrown = record.arg;
9169 resetTryEntry(entry);
9174 } // The context.catch method must only be called with a location
9175 // argument that corresponds to a known catch block.
9178 throw new Error("illegal catch attempt");
9180 delegateYield: function delegateYield(iterable, resultName, nextLoc) {
9182 iterator: values(iterable),
9183 resultName: resultName,
9187 if (this.method === "next") {
9188 // Deliberately forget the last sent value so that we don't
9189 // accidentally pass it on to the delegate.
9190 this.arg = undefined$1;
9193 return ContinueSentinel;
9195 }; // Regardless of whether this script is executing as a CommonJS module
9196 // or not, return the runtime object so that we can declare the variable
9197 // regeneratorRuntime in the outer scope, which allows this module to be
9198 // injected easily by `bin/regenerator --include-runtime script.js`.
9201 }( // If this script is executing as a CommonJS module, use module.exports
9202 // as the regeneratorRuntime namespace. Otherwise create a new empty
9203 // object. Either way, the resulting object will be used to initialize
9204 // the regeneratorRuntime variable at the top of this file.
9208 regeneratorRuntime = runtime;
9209 } catch (accidentalStrictMode) {
9210 // This module should not be running in strict mode, so the above
9211 // assignment should always work unless something is misconfigured. Just
9212 // in case runtime.js accidentally runs in strict mode, we can escape
9213 // strict mode using a global Function call. This could conceivably fail
9214 // if a Content Security Policy forbids using Function, but in that case
9215 // the proper solution is to fix the accidental strict mode problem. If
9216 // you've misconfigured your bundler to force strict mode and applied a
9217 // CSP to forbid Function, and you're not willing to fix either of those
9218 // problems, please detail your unique predicament in a GitHub issue.
9219 Function("r", "regeneratorRuntime = r")(runtime);
9223 var _marked = /*#__PURE__*/regeneratorRuntime.mark(numbers);
9225 function number (x) {
9226 return x === null ? NaN : +x;
9228 function numbers(values, valueof) {
9229 var _iterator, _step, value, index, _iterator2, _step2, _value;
9231 return regeneratorRuntime.wrap(function numbers$(_context) {
9233 switch (_context.prev = _context.next) {
9235 if (!(valueof === undefined)) {
9240 _iterator = _createForOfIteratorHelper(values);
9246 if ((_step = _iterator.n()).done) {
9251 value = _step.value;
9253 if (!(value != null && (value = +value) >= value)) {
9271 _context.t0 = _context["catch"](2);
9273 _iterator.e(_context.t0);
9280 return _context.finish(16);
9288 _iterator2 = _createForOfIteratorHelper(values);
9294 if ((_step2 = _iterator2.n()).done) {
9299 _value = _step2.value;
9301 if (!((_value = valueof(_value, ++index, values)) != null && (_value = +_value) >= _value)) {
9319 _context.t1 = _context["catch"](23);
9321 _iterator2.e(_context.t1);
9328 return _context.finish(37);
9332 return _context.stop();
9335 }, _marked, null, [[2, 13, 16, 19], [23, 34, 37, 40]]);
9338 var ascendingBisect = d3_bisector(d3_ascending);
9339 var bisectRight = ascendingBisect.right;
9340 var bisectCenter = d3_bisector(number).center;
9342 // `Array.prototype.fill` method
9343 // https://tc39.github.io/ecma262/#sec-array.prototype.fill
9344 _export({ target: 'Array', proto: true }, {
9348 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
9349 addToUnscopables('fill');
9351 var INCORRECT_ITERATION$1 = !checkCorrectnessOfIteration(function (iterable) {
9352 Array.from(iterable);
9355 // `Array.from` method
9356 // https://tc39.github.io/ecma262/#sec-array.from
9357 _export({ target: 'Array', stat: true, forced: INCORRECT_ITERATION$1 }, {
9361 var $some$1 = arrayIteration.some;
9365 var STRICT_METHOD$4 = arrayMethodIsStrict('some');
9366 var USES_TO_LENGTH$7 = arrayMethodUsesToLength('some');
9368 // `Array.prototype.some` method
9369 // https://tc39.github.io/ecma262/#sec-array.prototype.some
9370 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$4 || !USES_TO_LENGTH$7 }, {
9371 some: function some(callbackfn /* , thisArg */) {
9372 return $some$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
9376 // `Float64Array` constructor
9377 // https://tc39.github.io/ecma262/#sec-typedarray-objects
9378 typedArrayConstructor('Float64', function (init) {
9379 return function Float64Array(data, byteOffset, length) {
9380 return init(this, data, byteOffset, length);
9384 var exportTypedArrayStaticMethod$1 = arrayBufferViewCore.exportTypedArrayStaticMethod;
9387 // `%TypedArray%.from` method
9388 // https://tc39.github.io/ecma262/#sec-%typedarray%.from
9389 exportTypedArrayStaticMethod$1('from', typedArrayFrom, typedArrayConstructorsRequireWrappers);
9391 function d3_descending (a, b) {
9392 return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
9395 // https://github.com/python/cpython/blob/a74eea238f5baba15797e2e8b570d153bc8690a7/Modules/mathmodule.c#L1423
9396 var Adder = /*#__PURE__*/function () {
9398 _classCallCheck(this, Adder);
9400 this._partials = new Float64Array(32);
9404 _createClass(Adder, [{
9406 value: function add(x) {
9407 var p = this._partials;
9410 for (var j = 0; j < this._n && j < 32; j++) {
9413 lo = Math.abs(x) < Math.abs(y) ? x - (hi - y) : y - (hi - x);
9414 if (lo) p[i++] = lo;
9424 value: function valueOf() {
9425 var p = this._partials;
9443 if (n > 0 && (lo < 0 && p[n - 1] < 0 || lo > 0 && p[n - 1] > 0)) {
9446 if (y == x - hi) hi = x;
9457 // `Map` constructor
9458 // https://tc39.github.io/ecma262/#sec-map-objects
9459 var es_map = collection('Map', function (init) {
9460 return function Map() { return init(this, arguments.length ? arguments[0] : undefined); };
9461 }, collectionStrong);
9463 var e10 = Math.sqrt(50),
9466 function ticks (start, stop, count) {
9472 stop = +stop, start = +start, count = +count;
9473 if (start === stop && count > 0) return [start];
9474 if (reverse = stop < start) n = start, start = stop, stop = n;
9475 if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
9478 start = Math.ceil(start / step);
9479 stop = Math.floor(stop / step);
9480 ticks = new Array(n = Math.ceil(stop - start + 1));
9483 ticks[i] = (start + i) * step;
9487 start = Math.ceil(start * step);
9488 stop = Math.floor(stop * step);
9489 ticks = new Array(n = Math.ceil(stop - start + 1));
9492 ticks[i] = (start + i) / step;
9496 if (reverse) ticks.reverse();
9499 function tickIncrement(start, stop, count) {
9500 var step = (stop - start) / Math.max(0, count),
9501 power = Math.floor(Math.log(step) / Math.LN10),
9502 error = step / Math.pow(10, power);
9503 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);
9505 function tickStep(start, stop, count) {
9506 var step0 = Math.abs(stop - start) / Math.max(0, count),
9507 step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
9508 error = step0 / step1;
9509 if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2;
9510 return stop < start ? -step1 : step1;
9513 function max$4(values, valueof) {
9516 if (valueof === undefined) {
9517 var _iterator = _createForOfIteratorHelper(values),
9521 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9522 var value = _step.value;
9524 if (value != null && (max < value || max === undefined && value >= value)) {
9536 var _iterator2 = _createForOfIteratorHelper(values),
9540 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9541 var _value = _step2.value;
9543 if ((_value = valueof(_value, ++index, values)) != null && (max < _value || max === undefined && _value >= _value)) {
9557 function min$7(values, valueof) {
9560 if (valueof === undefined) {
9561 var _iterator = _createForOfIteratorHelper(values),
9565 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9566 var value = _step.value;
9568 if (value != null && (min > value || min === undefined && value >= value)) {
9580 var _iterator2 = _createForOfIteratorHelper(values),
9584 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9585 var _value = _step2.value;
9587 if ((_value = valueof(_value, ++index, values)) != null && (min > _value || min === undefined && _value >= _value)) {
9601 // ISC license, Copyright 2018 Vladimir Agafonkin.
9603 function quickselect(array, k) {
9604 var left = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
9605 var right = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : array.length - 1;
9606 var compare = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : d3_ascending;
9608 while (right > left) {
9609 if (right - left > 600) {
9610 var n = right - left + 1;
9611 var m = k - left + 1;
9612 var z = Math.log(n);
9613 var s = 0.5 * Math.exp(2 * z / 3);
9614 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
9615 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
9616 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
9617 quickselect(array, k, newLeft, newRight, compare);
9623 swap(array, left, k);
9624 if (compare(array[right], t) > 0) swap(array, left, right);
9627 swap(array, i, j), ++i, --j;
9629 while (compare(array[i], t) < 0) {
9633 while (compare(array[j], t) > 0) {
9638 if (compare(array[left], t) === 0) swap(array, left, j);else ++j, swap(array, j, right);
9639 if (j <= k) left = j + 1;
9640 if (k <= j) right = j - 1;
9646 function swap(array, i, j) {
9648 array[i] = array[j];
9652 function quantile(values, p, valueof) {
9653 values = Float64Array.from(numbers(values, valueof));
9654 if (!(n = values.length)) return;
9655 if ((p = +p) <= 0 || n < 2) return min$7(values);
9656 if (p >= 1) return max$4(values);
9660 value0 = max$4(quickselect(values, i0).subarray(0, i0 + 1)),
9661 value1 = min$7(values.subarray(i0 + 1));
9662 return value0 + (value1 - value0) * (i - i0);
9665 function d3_median (values, valueof) {
9666 return quantile(values, 0.5, valueof);
9669 var _marked$1 = /*#__PURE__*/regeneratorRuntime.mark(flatten);
9671 function flatten(arrays) {
9672 var _iterator, _step, array;
9674 return regeneratorRuntime.wrap(function flatten$(_context) {
9676 switch (_context.prev = _context.next) {
9678 _iterator = _createForOfIteratorHelper(arrays);
9684 if ((_step = _iterator.n()).done) {
9689 array = _step.value;
9690 return _context.delegateYield(array, "t0", 6);
9702 _context.t1 = _context["catch"](1);
9704 _iterator.e(_context.t1);
9711 return _context.finish(13);
9715 return _context.stop();
9718 }, _marked$1, null, [[1, 10, 13, 16]]);
9721 function merge(arrays) {
9722 return Array.from(flatten(arrays));
9725 function range (start, stop, step) {
9726 start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
9728 n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
9729 range = new Array(n);
9732 range[i] = start + i * step;
9739 var nativeSort = test$2.sort;
9742 var FAILS_ON_UNDEFINED = fails(function () {
9743 test$2.sort(undefined);
9746 var FAILS_ON_NULL = fails(function () {
9750 var STRICT_METHOD$5 = arrayMethodIsStrict('sort');
9752 var FORCED$a = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD$5;
9754 // `Array.prototype.sort` method
9755 // https://tc39.github.io/ecma262/#sec-array.prototype.sort
9756 _export({ target: 'Array', proto: true, forced: FORCED$a }, {
9757 sort: function sort(comparefn) {
9758 return comparefn === undefined
9759 ? nativeSort.call(toObject(this))
9760 : nativeSort.call(toObject(this), aFunction$1(comparefn));
9764 // `SameValue` abstract operation
9765 // https://tc39.github.io/ecma262/#sec-samevalue
9766 var sameValue = Object.is || function is(x, y) {
9767 // eslint-disable-next-line no-self-compare
9768 return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;
9771 var $hypot = Math.hypot;
9772 var abs$1 = Math.abs;
9773 var sqrt = Math.sqrt;
9776 // https://bugs.chromium.org/p/v8/issues/detail?id=9546
9777 var BUGGY = !!$hypot && $hypot(Infinity, NaN) !== Infinity;
9779 // `Math.hypot` method
9780 // https://tc39.github.io/ecma262/#sec-math.hypot
9781 _export({ target: 'Math', stat: true, forced: BUGGY }, {
9782 hypot: function hypot(value1, value2) { // eslint-disable-line no-unused-vars
9785 var aLen = arguments.length;
9789 arg = abs$1(arguments[i++]);
9792 sum = sum * div * div + 1;
9794 } else if (arg > 0) {
9799 return larg === Infinity ? Infinity : larg * sqrt(sum);
9803 // `Math.sign` method implementation
9804 // https://tc39.github.io/ecma262/#sec-math.sign
9805 var mathSign = Math.sign || function sign(x) {
9806 // eslint-disable-next-line no-self-compare
9807 return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
9810 // `Math.sign` method
9811 // https://tc39.github.io/ecma262/#sec-math.sign
9812 _export({ target: 'Math', stat: true }, {
9817 var epsilon2 = 1e-12;
9819 var halfPi = pi / 2;
9820 var quarterPi = pi / 4;
9822 var degrees = 180 / pi;
9823 var radians = pi / 180;
9824 var abs$2 = Math.abs;
9825 var atan = Math.atan;
9826 var atan2 = Math.atan2;
9829 var hypot = Math.hypot;
9830 var log$1 = Math.log;
9832 var sign = Math.sign || function (x) {
9833 return x > 0 ? 1 : x < 0 ? -1 : 0;
9835 var sqrt$1 = Math.sqrt;
9838 return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
9841 return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
9846 function streamGeometry(geometry, stream) {
9847 if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
9848 streamGeometryType[geometry.type](geometry, stream);
9852 var streamObjectType = {
9853 Feature: function Feature(object, stream) {
9854 streamGeometry(object.geometry, stream);
9856 FeatureCollection: function FeatureCollection(object, stream) {
9857 var features = object.features,
9859 n = features.length;
9862 streamGeometry(features[i].geometry, stream);
9866 var streamGeometryType = {
9867 Sphere: function Sphere(object, stream) {
9870 Point: function Point(object, stream) {
9871 object = object.coordinates;
9872 stream.point(object[0], object[1], object[2]);
9874 MultiPoint: function MultiPoint(object, stream) {
9875 var coordinates = object.coordinates,
9877 n = coordinates.length;
9880 object = coordinates[i], stream.point(object[0], object[1], object[2]);
9883 LineString: function LineString(object, stream) {
9884 streamLine(object.coordinates, stream, 0);
9886 MultiLineString: function MultiLineString(object, stream) {
9887 var coordinates = object.coordinates,
9889 n = coordinates.length;
9892 streamLine(coordinates[i], stream, 0);
9895 Polygon: function Polygon(object, stream) {
9896 streamPolygon(object.coordinates, stream);
9898 MultiPolygon: function MultiPolygon(object, stream) {
9899 var coordinates = object.coordinates,
9901 n = coordinates.length;
9904 streamPolygon(coordinates[i], stream);
9907 GeometryCollection: function GeometryCollection(object, stream) {
9908 var geometries = object.geometries,
9910 n = geometries.length;
9913 streamGeometry(geometries[i], stream);
9918 function streamLine(coordinates, stream, closed) {
9920 n = coordinates.length - closed,
9925 coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
9931 function streamPolygon(coordinates, stream) {
9933 n = coordinates.length;
9934 stream.polygonStart();
9937 streamLine(coordinates[i], stream, 1);
9940 stream.polygonEnd();
9943 function d3_geoStream (object, stream) {
9944 if (object && streamObjectType.hasOwnProperty(object.type)) {
9945 streamObjectType[object.type](object, stream);
9947 streamGeometry(object, stream);
9951 var areaRingSum = new Adder(); // hello?
9953 var areaSum = new Adder(),
9963 polygonStart: function polygonStart() {
9964 areaRingSum = new Adder();
9965 areaStream.lineStart = areaRingStart;
9966 areaStream.lineEnd = areaRingEnd;
9968 polygonEnd: function polygonEnd() {
9969 var areaRing = +areaRingSum;
9970 areaSum.add(areaRing < 0 ? tau + areaRing : areaRing);
9971 this.lineStart = this.lineEnd = this.point = noop;
9973 sphere: function sphere() {
9978 function areaRingStart() {
9979 areaStream.point = areaPointFirst;
9982 function areaRingEnd() {
9983 areaPoint(lambda00, phi00);
9986 function areaPointFirst(lambda, phi) {
9987 areaStream.point = areaPoint;
9988 lambda00 = lambda, phi00 = phi;
9989 lambda *= radians, phi *= radians;
9990 lambda0 = lambda, cosPhi0 = cos(phi = phi / 2 + quarterPi), sinPhi0 = sin(phi);
9993 function areaPoint(lambda, phi) {
9994 lambda *= radians, phi *= radians;
9995 phi = phi / 2 + quarterPi; // half the angular distance from south pole
9996 // Spherical excess E for a spherical triangle with vertices: south pole,
9997 // previous point, current point. Uses a formula derived from Cagnoli’s
9998 // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
10000 var dLambda = lambda - lambda0,
10001 sdLambda = dLambda >= 0 ? 1 : -1,
10002 adLambda = sdLambda * dLambda,
10005 k = sinPhi0 * sinPhi,
10006 u = cosPhi0 * cosPhi + k * cos(adLambda),
10007 v = k * sdLambda * sin(adLambda);
10008 areaRingSum.add(atan2(v, u)); // Advance the previous points.
10010 lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
10013 function d3_geoArea (object) {
10014 areaSum = new Adder();
10015 d3_geoStream(object, areaStream);
10016 return areaSum * 2;
10019 function spherical(cartesian) {
10020 return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
10022 function cartesian(spherical) {
10023 var lambda = spherical[0],
10024 phi = spherical[1],
10026 return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
10028 function cartesianDot(a, b) {
10029 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
10031 function cartesianCross(a, b) {
10032 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]];
10035 function cartesianAddInPlace(a, b) {
10036 a[0] += b[0], a[1] += b[1], a[2] += b[2];
10038 function cartesianScale(vector, k) {
10039 return [vector[0] * k, vector[1] * k, vector[2] * k];
10042 function cartesianNormalizeInPlace(d) {
10043 var l = sqrt$1(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
10044 d[0] /= l, d[1] /= l, d[2] /= l;
10047 var lambda0$1, phi0, lambda1, phi1, // bounds
10048 lambda2, // previous lambda-coordinate
10049 lambda00$1, phi00$1, // first point
10050 p0, // previous 3D point
10051 deltaSum, ranges, range$1;
10052 var boundsStream = {
10053 point: boundsPoint,
10054 lineStart: boundsLineStart,
10055 lineEnd: boundsLineEnd,
10056 polygonStart: function polygonStart() {
10057 boundsStream.point = boundsRingPoint;
10058 boundsStream.lineStart = boundsRingStart;
10059 boundsStream.lineEnd = boundsRingEnd;
10060 deltaSum = new Adder();
10061 areaStream.polygonStart();
10063 polygonEnd: function polygonEnd() {
10064 areaStream.polygonEnd();
10065 boundsStream.point = boundsPoint;
10066 boundsStream.lineStart = boundsLineStart;
10067 boundsStream.lineEnd = boundsLineEnd;
10068 if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);else if (deltaSum > epsilon) phi1 = 90;else if (deltaSum < -epsilon) phi0 = -90;
10069 range$1[0] = lambda0$1, range$1[1] = lambda1;
10071 sphere: function sphere() {
10072 lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
10076 function boundsPoint(lambda, phi) {
10077 ranges.push(range$1 = [lambda0$1 = lambda, lambda1 = lambda]);
10078 if (phi < phi0) phi0 = phi;
10079 if (phi > phi1) phi1 = phi;
10082 function linePoint(lambda, phi) {
10083 var p = cartesian([lambda * radians, phi * radians]);
10086 var normal = cartesianCross(p0, p),
10087 equatorial = [normal[1], -normal[0], 0],
10088 inflection = cartesianCross(equatorial, normal);
10089 cartesianNormalizeInPlace(inflection);
10090 inflection = spherical(inflection);
10091 var delta = lambda - lambda2,
10092 sign = delta > 0 ? 1 : -1,
10093 lambdai = inflection[0] * degrees * sign,
10095 antimeridian = abs$2(delta) > 180;
10097 if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10098 phii = inflection[1] * degrees;
10099 if (phii > phi1) phi1 = phii;
10100 } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
10101 phii = -inflection[1] * degrees;
10102 if (phii < phi0) phi0 = phii;
10104 if (phi < phi0) phi0 = phi;
10105 if (phi > phi1) phi1 = phi;
10108 if (antimeridian) {
10109 if (lambda < lambda2) {
10110 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10112 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10115 if (lambda1 >= lambda0$1) {
10116 if (lambda < lambda0$1) lambda0$1 = lambda;
10117 if (lambda > lambda1) lambda1 = lambda;
10119 if (lambda > lambda2) {
10120 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
10122 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
10127 ranges.push(range$1 = [lambda0$1 = lambda, lambda1 = lambda]);
10130 if (phi < phi0) phi0 = phi;
10131 if (phi > phi1) phi1 = phi;
10132 p0 = p, lambda2 = lambda;
10135 function boundsLineStart() {
10136 boundsStream.point = linePoint;
10139 function boundsLineEnd() {
10140 range$1[0] = lambda0$1, range$1[1] = lambda1;
10141 boundsStream.point = boundsPoint;
10145 function boundsRingPoint(lambda, phi) {
10147 var delta = lambda - lambda2;
10148 deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
10150 lambda00$1 = lambda, phi00$1 = phi;
10153 areaStream.point(lambda, phi);
10154 linePoint(lambda, phi);
10157 function boundsRingStart() {
10158 areaStream.lineStart();
10161 function boundsRingEnd() {
10162 boundsRingPoint(lambda00$1, phi00$1);
10163 areaStream.lineEnd();
10164 if (abs$2(deltaSum) > epsilon) lambda0$1 = -(lambda1 = 180);
10165 range$1[0] = lambda0$1, range$1[1] = lambda1;
10167 } // Finds the left-right distance between two longitudes.
10168 // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
10169 // the distance between ±180° to be 360°.
10172 function angle(lambda0, lambda1) {
10173 return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
10176 function rangeCompare(a, b) {
10177 return a[0] - b[0];
10180 function rangeContains(range, x) {
10181 return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
10184 function d3_geoBounds (feature) {
10185 var i, n, a, b, merged, deltaMax, delta;
10186 phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
10188 d3_geoStream(feature, boundsStream); // First, sort ranges by their minimum longitudes.
10190 if (n = ranges.length) {
10191 ranges.sort(rangeCompare); // Then, merge any ranges that overlap.
10193 for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
10196 if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
10197 if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
10198 if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
10200 merged.push(a = b);
10202 } // Finally, find the largest gap between the merged ranges.
10203 // The final bounding box will be the inverse of this gap.
10206 for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
10208 if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
10212 ranges = range$1 = null;
10213 return lambda0$1 === Infinity || phi0 === Infinity ? [[NaN, NaN], [NaN, NaN]] : [[lambda0$1, phi0], [lambda1, phi1]];
10216 var W0, W1, X0, Y0, Z0, X1, Y1, Z1, X2, Y2, Z2, lambda00$2, phi00$2, // first point
10217 x0, y0, z0; // previous point
10219 var centroidStream = {
10221 point: centroidPoint,
10222 lineStart: centroidLineStart,
10223 lineEnd: centroidLineEnd,
10224 polygonStart: function polygonStart() {
10225 centroidStream.lineStart = centroidRingStart;
10226 centroidStream.lineEnd = centroidRingEnd;
10228 polygonEnd: function polygonEnd() {
10229 centroidStream.lineStart = centroidLineStart;
10230 centroidStream.lineEnd = centroidLineEnd;
10232 }; // Arithmetic mean of Cartesian vectors.
10234 function centroidPoint(lambda, phi) {
10235 lambda *= radians, phi *= radians;
10236 var cosPhi = cos(phi);
10237 centroidPointCartesian(cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi));
10240 function centroidPointCartesian(x, y, z) {
10242 X0 += (x - X0) / W0;
10243 Y0 += (y - Y0) / W0;
10244 Z0 += (z - Z0) / W0;
10247 function centroidLineStart() {
10248 centroidStream.point = centroidLinePointFirst;
10251 function centroidLinePointFirst(lambda, phi) {
10252 lambda *= radians, phi *= radians;
10253 var cosPhi = cos(phi);
10254 x0 = cosPhi * cos(lambda);
10255 y0 = cosPhi * sin(lambda);
10257 centroidStream.point = centroidLinePoint;
10258 centroidPointCartesian(x0, y0, z0);
10261 function centroidLinePoint(lambda, phi) {
10262 lambda *= radians, phi *= radians;
10263 var cosPhi = cos(phi),
10264 x = cosPhi * cos(lambda),
10265 y = cosPhi * sin(lambda),
10267 w = atan2(sqrt$1((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
10269 X1 += w * (x0 + (x0 = x));
10270 Y1 += w * (y0 + (y0 = y));
10271 Z1 += w * (z0 + (z0 = z));
10272 centroidPointCartesian(x0, y0, z0);
10275 function centroidLineEnd() {
10276 centroidStream.point = centroidPoint;
10277 } // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
10278 // J. Applied Mechanics 42, 239 (1975).
10281 function centroidRingStart() {
10282 centroidStream.point = centroidRingPointFirst;
10285 function centroidRingEnd() {
10286 centroidRingPoint(lambda00$2, phi00$2);
10287 centroidStream.point = centroidPoint;
10290 function centroidRingPointFirst(lambda, phi) {
10291 lambda00$2 = lambda, phi00$2 = phi;
10292 lambda *= radians, phi *= radians;
10293 centroidStream.point = centroidRingPoint;
10294 var cosPhi = cos(phi);
10295 x0 = cosPhi * cos(lambda);
10296 y0 = cosPhi * sin(lambda);
10298 centroidPointCartesian(x0, y0, z0);
10301 function centroidRingPoint(lambda, phi) {
10302 lambda *= radians, phi *= radians;
10303 var cosPhi = cos(phi),
10304 x = cosPhi * cos(lambda),
10305 y = cosPhi * sin(lambda),
10307 cx = y0 * z - z0 * y,
10308 cy = z0 * x - x0 * z,
10309 cz = x0 * y - y0 * x,
10310 m = hypot(cx, cy, cz),
10312 // line weight = angle
10313 v = m && -w / m; // area weight multiplier
10319 X1 += w * (x0 + (x0 = x));
10320 Y1 += w * (y0 + (y0 = y));
10321 Z1 += w * (z0 + (z0 = z));
10322 centroidPointCartesian(x0, y0, z0);
10325 function d3_geoCentroid (object) {
10326 W0 = W1 = X0 = Y0 = Z0 = X1 = Y1 = Z1 = 0;
10330 d3_geoStream(object, centroidStream);
10334 m = hypot(x, y, z); // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
10336 if (m < epsilon2) {
10337 x = X1, y = Y1, z = Z1; // If the feature has zero length, fall back to arithmetic mean of point vectors.
10339 if (W1 < epsilon) x = X0, y = Y0, z = Z0;
10340 m = hypot(x, y, z); // If the feature still has an undefined ccentroid, then return.
10342 if (m < epsilon2) return [NaN, NaN];
10345 return [atan2(y, x) * degrees, asin(z / m) * degrees];
10348 function compose (a, b) {
10349 function compose(x, y) {
10350 return x = a(x, y), b(x[0], x[1]);
10353 if (a.invert && b.invert) compose.invert = function (x, y) {
10354 return x = b.invert(x, y), x && a.invert(x[0], x[1]);
10359 function rotationIdentity(lambda, phi) {
10360 return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi];
10363 rotationIdentity.invert = rotationIdentity;
10364 function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
10365 return (deltaLambda %= tau) ? deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma)) : rotationLambda(deltaLambda) : deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma) : rotationIdentity;
10368 function forwardRotationLambda(deltaLambda) {
10369 return function (lambda, phi) {
10370 return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi];
10374 function rotationLambda(deltaLambda) {
10375 var rotation = forwardRotationLambda(deltaLambda);
10376 rotation.invert = forwardRotationLambda(-deltaLambda);
10380 function rotationPhiGamma(deltaPhi, deltaGamma) {
10381 var cosDeltaPhi = cos(deltaPhi),
10382 sinDeltaPhi = sin(deltaPhi),
10383 cosDeltaGamma = cos(deltaGamma),
10384 sinDeltaGamma = sin(deltaGamma);
10386 function rotation(lambda, phi) {
10387 var cosPhi = cos(phi),
10388 x = cos(lambda) * cosPhi,
10389 y = sin(lambda) * cosPhi,
10391 k = z * cosDeltaPhi + x * sinDeltaPhi;
10392 return [atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi), asin(k * cosDeltaGamma + y * sinDeltaGamma)];
10395 rotation.invert = function (lambda, phi) {
10396 var cosPhi = cos(phi),
10397 x = cos(lambda) * cosPhi,
10398 y = sin(lambda) * cosPhi,
10400 k = z * cosDeltaGamma - y * sinDeltaGamma;
10401 return [atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi), asin(k * cosDeltaPhi - x * sinDeltaPhi)];
10407 function rotation (rotate) {
10408 rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
10410 function forward(coordinates) {
10411 coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
10412 return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
10415 forward.invert = function (coordinates) {
10416 coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
10417 return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
10423 function circleStream(stream, radius, delta, direction, t0, t1) {
10424 if (!delta) return;
10425 var cosRadius = cos(radius),
10426 sinRadius = sin(radius),
10427 step = direction * delta;
10430 t0 = radius + direction * tau;
10431 t1 = radius - step / 2;
10433 t0 = circleRadius(cosRadius, t0);
10434 t1 = circleRadius(cosRadius, t1);
10435 if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau;
10438 for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
10439 point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
10440 stream.point(point[0], point[1]);
10442 } // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
10444 function circleRadius(cosRadius, point) {
10445 point = cartesian(point), point[0] -= cosRadius;
10446 cartesianNormalizeInPlace(point);
10447 var radius = acos(-point[1]);
10448 return ((-point[2] < 0 ? -radius : radius) + tau - epsilon) % tau;
10451 function clipBuffer () {
10455 point: function point(x, y, m) {
10456 line.push([x, y, m]);
10458 lineStart: function lineStart() {
10459 lines.push(line = []);
10462 rejoin: function rejoin() {
10463 if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
10465 result: function result() {
10466 var result = lines;
10474 function pointEqual (a, b) {
10475 return abs$2(a[0] - b[0]) < epsilon && abs$2(a[1] - b[1]) < epsilon;
10478 function Intersection(point, points, other, entry) {
10481 this.o = other; // another intersection
10483 this.e = entry; // is an entry?
10485 this.v = false; // visited
10487 this.n = this.p = null; // next & previous
10488 } // A generalized polygon clipping algorithm: given a polygon that has been cut
10489 // into its visible line segments, and rejoins the segments by interpolating
10490 // along the clip edge.
10493 function clipRejoin (segments, compareIntersection, startInside, interpolate, stream) {
10498 segments.forEach(function (segment) {
10499 if ((n = segment.length - 1) <= 0) return;
10505 if (pointEqual(p0, p1)) {
10506 if (!p0[2] && !p1[2]) {
10507 stream.lineStart();
10509 for (i = 0; i < n; ++i) {
10510 stream.point((p0 = segment[i])[0], p0[1]);
10515 } // handle degenerate cases by moving the point
10518 p1[0] += 2 * epsilon;
10521 subject.push(x = new Intersection(p0, segment, null, true));
10522 clip.push(x.o = new Intersection(p0, null, x, false));
10523 subject.push(x = new Intersection(p1, segment, null, false));
10524 clip.push(x.o = new Intersection(p1, null, x, true));
10526 if (!subject.length) return;
10527 clip.sort(compareIntersection);
10531 for (i = 0, n = clip.length; i < n; ++i) {
10532 clip[i].e = startInside = !startInside;
10535 var start = subject[0],
10540 // Find first unvisited intersection.
10541 var current = start,
10544 while (current.v) {
10545 if ((current = current.n) === start) return;
10548 points = current.z;
10549 stream.lineStart();
10552 current.v = current.o.v = true;
10556 for (i = 0, n = points.length; i < n; ++i) {
10557 stream.point((point = points[i])[0], point[1]);
10560 interpolate(current.x, current.n.x, 1, stream);
10563 current = current.n;
10566 points = current.p.z;
10568 for (i = points.length - 1; i >= 0; --i) {
10569 stream.point((point = points[i])[0], point[1]);
10572 interpolate(current.x, current.p.x, -1, stream);
10575 current = current.p;
10578 current = current.o;
10579 points = current.z;
10580 isSubject = !isSubject;
10581 } while (!current.v);
10587 function link(array) {
10588 if (!(n = array.length)) return;
10595 a.n = b = array[i];
10600 a.n = b = array[0];
10604 function longitude(point) {
10605 if (abs$2(point[0]) <= pi) return point[0];else return sign(point[0]) * ((abs$2(point[0]) + pi) % tau - pi);
10608 function polygonContains (polygon, point) {
10609 var lambda = longitude(point),
10612 normal = [sin(lambda), -cos(lambda), 0],
10615 var sum = new Adder();
10616 if (sinPhi === 1) phi = halfPi + epsilon;else if (sinPhi === -1) phi = -halfPi - epsilon;
10618 for (var i = 0, n = polygon.length; i < n; ++i) {
10619 if (!(m = (ring = polygon[i]).length)) continue;
10622 point0 = ring[m - 1],
10623 lambda0 = longitude(point0),
10624 phi0 = point0[1] / 2 + quarterPi,
10625 sinPhi0 = sin(phi0),
10626 cosPhi0 = cos(phi0);
10628 for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
10629 var point1 = ring[j],
10630 lambda1 = longitude(point1),
10631 phi1 = point1[1] / 2 + quarterPi,
10632 sinPhi1 = sin(phi1),
10633 cosPhi1 = cos(phi1),
10634 delta = lambda1 - lambda0,
10635 sign = delta >= 0 ? 1 : -1,
10636 absDelta = sign * delta,
10637 antimeridian = absDelta > pi,
10638 k = sinPhi0 * sinPhi1;
10639 sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
10640 angle += antimeridian ? delta + sign * tau : delta; // Are the longitudes either side of the point’s meridian (lambda),
10641 // and are the latitudes smaller than the parallel (phi)?
10643 if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
10644 var arc = cartesianCross(cartesian(point0), cartesian(point1));
10645 cartesianNormalizeInPlace(arc);
10646 var intersection = cartesianCross(normal, arc);
10647 cartesianNormalizeInPlace(intersection);
10648 var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
10650 if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
10651 winding += antimeridian ^ delta >= 0 ? 1 : -1;
10655 } // First, determine whether the South pole is inside or outside:
10657 // It is inside if:
10658 // * the polygon winds around it in a clockwise direction.
10659 // * the polygon does not (cumulatively) wind around it, but has a negative
10660 // (counter-clockwise) area.
10662 // Second, count the (signed) number of times a segment crosses a lambda
10663 // from the point to the South pole. If it is zero, then the point is the
10664 // same side as the South pole.
10667 return (angle < -epsilon || angle < epsilon && sum < -epsilon2) ^ winding & 1;
10670 function clip (pointVisible, clipLine, interpolate, start) {
10671 return function (sink) {
10672 var line = clipLine(sink),
10673 ringBuffer = clipBuffer(),
10674 ringSink = clipLine(ringBuffer),
10675 polygonStarted = false,
10681 lineStart: lineStart,
10683 polygonStart: function polygonStart() {
10684 clip.point = pointRing;
10685 clip.lineStart = ringStart;
10686 clip.lineEnd = ringEnd;
10690 polygonEnd: function polygonEnd() {
10691 clip.point = point;
10692 clip.lineStart = lineStart;
10693 clip.lineEnd = lineEnd;
10694 segments = merge(segments);
10695 var startInside = polygonContains(polygon, start);
10697 if (segments.length) {
10698 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10699 clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
10700 } else if (startInside) {
10701 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10703 interpolate(null, null, 1, sink);
10707 if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
10708 segments = polygon = null;
10710 sphere: function sphere() {
10711 sink.polygonStart();
10713 interpolate(null, null, 1, sink);
10719 function point(lambda, phi) {
10720 if (pointVisible(lambda, phi)) sink.point(lambda, phi);
10723 function pointLine(lambda, phi) {
10724 line.point(lambda, phi);
10727 function lineStart() {
10728 clip.point = pointLine;
10732 function lineEnd() {
10733 clip.point = point;
10737 function pointRing(lambda, phi) {
10738 ring.push([lambda, phi]);
10739 ringSink.point(lambda, phi);
10742 function ringStart() {
10743 ringSink.lineStart();
10747 function ringEnd() {
10748 pointRing(ring[0][0], ring[0][1]);
10749 ringSink.lineEnd();
10750 var clean = ringSink.clean(),
10751 ringSegments = ringBuffer.result(),
10753 n = ringSegments.length,
10758 polygon.push(ring);
10760 if (!n) return; // No intersections.
10763 segment = ringSegments[0];
10765 if ((m = segment.length - 1) > 0) {
10766 if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
10769 for (i = 0; i < m; ++i) {
10770 sink.point((point = segment[i])[0], point[1]);
10777 } // Rejoin connected segments.
10778 // TODO reuse ringBuffer.rejoin()?
10781 if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
10782 segments.push(ringSegments.filter(validSegment));
10789 function validSegment(segment) {
10790 return segment.length > 1;
10791 } // Intersections are sorted along the clip edge. For both antimeridian cutting
10792 // and circle clipping, the same comparison is used.
10795 function compareIntersection(a, b) {
10796 return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon : halfPi - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon : halfPi - b[1]);
10799 var clipAntimeridian = clip(function () {
10801 }, clipAntimeridianLine, clipAntimeridianInterpolate, [-pi, -halfPi]); // Takes a line and cuts into visible segments. Return values: 0 - there were
10802 // intersections or the line was empty; 1 - no intersections; 2 - there were
10803 // intersections, and the first and last segments should be rejoined.
10805 function clipAntimeridianLine(stream) {
10809 _clean; // no intersections
10813 lineStart: function lineStart() {
10814 stream.lineStart();
10817 point: function point(lambda1, phi1) {
10818 var sign1 = lambda1 > 0 ? pi : -pi,
10819 delta = abs$2(lambda1 - lambda0);
10821 if (abs$2(delta - pi) < epsilon) {
10822 // line crosses a pole
10823 stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
10824 stream.point(sign0, phi0);
10826 stream.lineStart();
10827 stream.point(sign1, phi0);
10828 stream.point(lambda1, phi0);
10830 } else if (sign0 !== sign1 && delta >= pi) {
10831 // line crosses antimeridian
10832 if (abs$2(lambda0 - sign0) < epsilon) lambda0 -= sign0 * epsilon; // handle degeneracies
10834 if (abs$2(lambda1 - sign1) < epsilon) lambda1 -= sign1 * epsilon;
10835 phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
10836 stream.point(sign0, phi0);
10838 stream.lineStart();
10839 stream.point(sign1, phi0);
10843 stream.point(lambda0 = lambda1, phi0 = phi1);
10846 lineEnd: function lineEnd() {
10848 lambda0 = phi0 = NaN;
10850 clean: function clean() {
10851 return 2 - _clean; // if intersections, rejoin first and last segments
10856 function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
10859 sinLambda0Lambda1 = sin(lambda0 - lambda1);
10860 return abs$2(sinLambda0Lambda1) > epsilon ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1) - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0)) / (cosPhi0 * cosPhi1 * sinLambda0Lambda1)) : (phi0 + phi1) / 2;
10863 function clipAntimeridianInterpolate(from, to, direction, stream) {
10866 if (from == null) {
10867 phi = direction * halfPi;
10868 stream.point(-pi, phi);
10869 stream.point(0, phi);
10870 stream.point(pi, phi);
10871 stream.point(pi, 0);
10872 stream.point(pi, -phi);
10873 stream.point(0, -phi);
10874 stream.point(-pi, -phi);
10875 stream.point(-pi, 0);
10876 stream.point(-pi, phi);
10877 } else if (abs$2(from[0] - to[0]) > epsilon) {
10878 var lambda = from[0] < to[0] ? pi : -pi;
10879 phi = direction * lambda / 2;
10880 stream.point(-lambda, phi);
10881 stream.point(0, phi);
10882 stream.point(lambda, phi);
10884 stream.point(to[0], to[1]);
10888 function clipCircle (radius) {
10889 var cr = cos(radius),
10890 delta = 6 * radians,
10891 smallRadius = cr > 0,
10892 notHemisphere = abs$2(cr) > epsilon; // TODO optimise for this common case
10894 function interpolate(from, to, direction, stream) {
10895 circleStream(stream, radius, delta, direction, from, to);
10898 function visible(lambda, phi) {
10899 return cos(lambda) * cos(phi) > cr;
10900 } // Takes a line and cuts into visible segments. Return values used for polygon
10901 // clipping: 0 - there were intersections or the line was empty; 1 - no
10902 // intersections 2 - there were intersections, and the first and last segments
10903 // should be rejoined.
10906 function clipLine(stream) {
10907 var point0, // previous point
10908 c0, // code for previous point
10909 v0, // visibility of previous point
10910 v00, // visibility of first point
10911 _clean; // no intersections
10915 lineStart: function lineStart() {
10919 point: function point(lambda, phi) {
10920 var point1 = [lambda, phi],
10922 v = visible(lambda, phi),
10923 c = smallRadius ? v ? 0 : code(lambda, phi) : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
10924 if (!point0 && (v00 = v0 = v)) stream.lineStart();
10927 point2 = intersect(point0, point1);
10928 if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) point1[2] = 1;
10935 // outside going in
10936 stream.lineStart();
10937 point2 = intersect(point1, point0);
10938 stream.point(point2[0], point2[1]);
10940 // inside going out
10941 point2 = intersect(point0, point1);
10942 stream.point(point2[0], point2[1], 2);
10947 } else if (notHemisphere && point0 && smallRadius ^ v) {
10948 var t; // If the codes for two points are different, or are both zero,
10949 // and there this segment intersects with the small circle.
10951 if (!(c & c0) && (t = intersect(point1, point0, true))) {
10955 stream.lineStart();
10956 stream.point(t[0][0], t[0][1]);
10957 stream.point(t[1][0], t[1][1]);
10960 stream.point(t[1][0], t[1][1]);
10962 stream.lineStart();
10963 stream.point(t[0][0], t[0][1], 3);
10968 if (v && (!point0 || !pointEqual(point0, point1))) {
10969 stream.point(point1[0], point1[1]);
10972 point0 = point1, v0 = v, c0 = c;
10974 lineEnd: function lineEnd() {
10975 if (v0) stream.lineEnd();
10978 // Rejoin first and last segments if there were intersections and the first
10979 // and last points were visible.
10980 clean: function clean() {
10981 return _clean | (v00 && v0) << 1;
10984 } // Intersects the great circle between a and b with the clip circle.
10987 function intersect(a, b, two) {
10988 var pa = cartesian(a),
10989 pb = cartesian(b); // We have two planes, n1.p = d1 and n2.p = d2.
10990 // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
10992 var n1 = [1, 0, 0],
10994 n2 = cartesianCross(pa, pb),
10995 n2n2 = cartesianDot(n2, n2),
10997 // cartesianDot(n1, n2),
10998 determinant = n2n2 - n1n2 * n1n2; // Two polar points.
11000 if (!determinant) return !two && a;
11001 var c1 = cr * n2n2 / determinant,
11002 c2 = -cr * n1n2 / determinant,
11003 n1xn2 = cartesianCross(n1, n2),
11004 A = cartesianScale(n1, c1),
11005 B = cartesianScale(n2, c2);
11006 cartesianAddInPlace(A, B); // Solve |p(t)|^2 = 1.
11009 w = cartesianDot(A, u),
11010 uu = cartesianDot(u, u),
11011 t2 = w * w - uu * (cartesianDot(A, A) - 1);
11012 if (t2 < 0) return;
11013 var t = sqrt$1(t2),
11014 q = cartesianScale(u, (-w - t) / uu);
11015 cartesianAddInPlace(q, A);
11017 if (!two) return q; // Two intersection points.
11019 var lambda0 = a[0],
11024 if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
11025 var delta = lambda1 - lambda0,
11026 polar = abs$2(delta - pi) < epsilon,
11027 meridian = polar || delta < epsilon;
11028 if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z; // Check that the first point is between a and b.
11030 if (meridian ? polar ? phi0 + phi1 > 0 ^ q[1] < (abs$2(q[0] - lambda0) < epsilon ? phi0 : phi1) : phi0 <= q[1] && q[1] <= phi1 : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
11031 var q1 = cartesianScale(u, (-w + t) / uu);
11032 cartesianAddInPlace(q1, A);
11033 return [q, spherical(q1)];
11035 } // Generates a 4-bit vector representing the location of a point relative to
11036 // the small circle's bounding box.
11039 function code(lambda, phi) {
11040 var r = smallRadius ? radius : pi - radius,
11042 if (lambda < -r) code |= 1; // left
11043 else if (lambda > r) code |= 2; // right
11045 if (phi < -r) code |= 4; // below
11046 else if (phi > r) code |= 8; // above
11051 return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
11054 function clipLine (a, b, x0, y0, x1, y1) {
11065 if (!dx && r > 0) return;
11069 if (r < t0) return;
11070 if (r < t1) t1 = r;
11071 } else if (dx > 0) {
11072 if (r > t1) return;
11073 if (r > t0) t0 = r;
11077 if (!dx && r < 0) return;
11081 if (r > t1) return;
11082 if (r > t0) t0 = r;
11083 } else if (dx > 0) {
11084 if (r < t0) return;
11085 if (r < t1) t1 = r;
11089 if (!dy && r > 0) return;
11093 if (r < t0) return;
11094 if (r < t1) t1 = r;
11095 } else if (dy > 0) {
11096 if (r > t1) return;
11097 if (r > t0) t0 = r;
11101 if (!dy && r < 0) return;
11105 if (r > t1) return;
11106 if (r > t0) t0 = r;
11107 } else if (dy > 0) {
11108 if (r < t0) return;
11109 if (r < t1) t1 = r;
11112 if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
11113 if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
11118 clipMin = -clipMax; // TODO Use d3-polygon’s polygonContains here for the ring check?
11119 // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
11121 function clipRectangle(x0, y0, x1, y1) {
11122 function visible(x, y) {
11123 return x0 <= x && x <= x1 && y0 <= y && y <= y1;
11126 function interpolate(from, to, direction, stream) {
11130 if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoint(from, to) < 0 ^ direction > 0) {
11132 stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
11133 } while ((a = (a + direction + 4) % 4) !== a1);
11135 stream.point(to[0], to[1]);
11139 function corner(p, direction) {
11140 return abs$2(p[0] - x0) < epsilon ? direction > 0 ? 0 : 3 : abs$2(p[0] - x1) < epsilon ? direction > 0 ? 2 : 1 : abs$2(p[1] - y0) < epsilon ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
11143 function compareIntersection(a, b) {
11144 return comparePoint(a.x, b.x);
11147 function comparePoint(a, b) {
11148 var ca = corner(a, 1),
11150 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];
11153 return function (stream) {
11154 var activeStream = stream,
11155 bufferStream = clipBuffer(),
11171 lineStart: lineStart,
11173 polygonStart: polygonStart,
11174 polygonEnd: polygonEnd
11177 function point(x, y) {
11178 if (visible(x, y)) activeStream.point(x, y);
11181 function polygonInside() {
11184 for (var i = 0, n = polygon.length; i < n; ++i) {
11185 for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
11186 a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
11189 if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding;
11191 if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding;
11197 } // Buffer geometry within a polygon and then clip it en masse.
11200 function polygonStart() {
11201 activeStream = bufferStream, segments = [], polygon = [], clean = true;
11204 function polygonEnd() {
11205 var startInside = polygonInside(),
11206 cleanInside = clean && startInside,
11207 visible = (segments = merge(segments)).length;
11209 if (cleanInside || visible) {
11210 stream.polygonStart();
11213 stream.lineStart();
11214 interpolate(null, null, 1, stream);
11219 clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
11222 stream.polygonEnd();
11225 activeStream = stream, segments = polygon = ring = null;
11228 function lineStart() {
11229 clipStream.point = linePoint;
11230 if (polygon) polygon.push(ring = []);
11234 } // TODO rather than special-case polygons, simply handle them separately.
11235 // Ideally, coincident intersection points should be jittered to avoid
11236 // clipping issues.
11239 function lineEnd() {
11241 linePoint(x__, y__);
11242 if (v__ && v_) bufferStream.rejoin();
11243 segments.push(bufferStream.result());
11246 clipStream.point = point;
11247 if (v_) activeStream.lineEnd();
11250 function linePoint(x, y) {
11251 var v = visible(x, y);
11252 if (polygon) ring.push([x, y]);
11255 x__ = x, y__ = y, v__ = v;
11259 activeStream.lineStart();
11260 activeStream.point(x, y);
11263 if (v && v_) activeStream.point(x, y);else {
11264 var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
11265 b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
11267 if (clipLine(a, b, x0, y0, x1, y1)) {
11269 activeStream.lineStart();
11270 activeStream.point(a[0], a[1]);
11273 activeStream.point(b[0], b[1]);
11274 if (!v) activeStream.lineEnd();
11277 activeStream.lineStart();
11278 activeStream.point(x, y);
11284 x_ = x, y_ = y, v_ = v;
11291 var lengthSum, lambda0$2, sinPhi0$1, cosPhi0$1;
11292 var lengthStream = {
11295 lineStart: lengthLineStart,
11297 polygonStart: noop,
11301 function lengthLineStart() {
11302 lengthStream.point = lengthPointFirst;
11303 lengthStream.lineEnd = lengthLineEnd;
11306 function lengthLineEnd() {
11307 lengthStream.point = lengthStream.lineEnd = noop;
11310 function lengthPointFirst(lambda, phi) {
11311 lambda *= radians, phi *= radians;
11312 lambda0$2 = lambda, sinPhi0$1 = sin(phi), cosPhi0$1 = cos(phi);
11313 lengthStream.point = lengthPoint;
11316 function lengthPoint(lambda, phi) {
11317 lambda *= radians, phi *= radians;
11318 var sinPhi = sin(phi),
11320 delta = abs$2(lambda - lambda0$2),
11321 cosDelta = cos(delta),
11322 sinDelta = sin(delta),
11323 x = cosPhi * sinDelta,
11324 y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
11325 z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
11326 lengthSum.add(atan2(sqrt$1(x * x + y * y), z));
11327 lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
11330 function d3_geoLength (object) {
11331 lengthSum = new Adder();
11332 d3_geoStream(object, lengthStream);
11336 var identity = (function (x) {
11340 var areaSum$1 = new Adder(),
11341 areaRingSum$1 = new Adder(),
11346 var areaStream$1 = {
11350 polygonStart: function polygonStart() {
11351 areaStream$1.lineStart = areaRingStart$1;
11352 areaStream$1.lineEnd = areaRingEnd$1;
11354 polygonEnd: function polygonEnd() {
11355 areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop;
11356 areaSum$1.add(abs$2(areaRingSum$1));
11357 areaRingSum$1 = new Adder();
11359 result: function result() {
11360 var area = areaSum$1 / 2;
11361 areaSum$1 = new Adder();
11366 function areaRingStart$1() {
11367 areaStream$1.point = areaPointFirst$1;
11370 function areaPointFirst$1(x, y) {
11371 areaStream$1.point = areaPoint$1;
11372 x00 = x0$1 = x, y00 = y0$1 = y;
11375 function areaPoint$1(x, y) {
11376 areaRingSum$1.add(y0$1 * x - x0$1 * y);
11377 x0$1 = x, y0$1 = y;
11380 function areaRingEnd$1() {
11381 areaPoint$1(x00, y00);
11384 var x0$2 = Infinity,
11388 var boundsStream$1 = {
11389 point: boundsPoint$1,
11392 polygonStart: noop,
11394 result: function result() {
11395 var bounds = [[x0$2, y0$2], [x1, y1]];
11396 x1 = y1 = -(y0$2 = x0$2 = Infinity);
11401 function boundsPoint$1(x, y) {
11402 if (x < x0$2) x0$2 = x;
11403 if (x > x1) x1 = x;
11404 if (y < y0$2) y0$2 = y;
11405 if (y > y1) y1 = y;
11421 var centroidStream$1 = {
11422 point: centroidPoint$1,
11423 lineStart: centroidLineStart$1,
11424 lineEnd: centroidLineEnd$1,
11425 polygonStart: function polygonStart() {
11426 centroidStream$1.lineStart = centroidRingStart$1;
11427 centroidStream$1.lineEnd = centroidRingEnd$1;
11429 polygonEnd: function polygonEnd() {
11430 centroidStream$1.point = centroidPoint$1;
11431 centroidStream$1.lineStart = centroidLineStart$1;
11432 centroidStream$1.lineEnd = centroidLineEnd$1;
11434 result: function result() {
11435 var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1] : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1] : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1] : [NaN, NaN];
11436 X0$1 = Y0$1 = Z0$1 = X1$1 = Y1$1 = Z1$1 = X2$1 = Y2$1 = Z2$1 = 0;
11441 function centroidPoint$1(x, y) {
11447 function centroidLineStart$1() {
11448 centroidStream$1.point = centroidPointFirstLine;
11451 function centroidPointFirstLine(x, y) {
11452 centroidStream$1.point = centroidPointLine;
11453 centroidPoint$1(x0$3 = x, y0$3 = y);
11456 function centroidPointLine(x, y) {
11459 z = sqrt$1(dx * dx + dy * dy);
11460 X1$1 += z * (x0$3 + x) / 2;
11461 Y1$1 += z * (y0$3 + y) / 2;
11463 centroidPoint$1(x0$3 = x, y0$3 = y);
11466 function centroidLineEnd$1() {
11467 centroidStream$1.point = centroidPoint$1;
11470 function centroidRingStart$1() {
11471 centroidStream$1.point = centroidPointFirstRing;
11474 function centroidRingEnd$1() {
11475 centroidPointRing(x00$1, y00$1);
11478 function centroidPointFirstRing(x, y) {
11479 centroidStream$1.point = centroidPointRing;
11480 centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
11483 function centroidPointRing(x, y) {
11486 z = sqrt$1(dx * dx + dy * dy);
11487 X1$1 += z * (x0$3 + x) / 2;
11488 Y1$1 += z * (y0$3 + y) / 2;
11490 z = y0$3 * x - x0$3 * y;
11491 X2$1 += z * (x0$3 + x);
11492 Y2$1 += z * (y0$3 + y);
11494 centroidPoint$1(x0$3 = x, y0$3 = y);
11497 function PathContext(context) {
11498 this._context = context;
11500 PathContext.prototype = {
11502 pointRadius: function pointRadius(_) {
11503 return this._radius = _, this;
11505 polygonStart: function polygonStart() {
11508 polygonEnd: function polygonEnd() {
11511 lineStart: function lineStart() {
11514 lineEnd: function lineEnd() {
11515 if (this._line === 0) this._context.closePath();
11518 point: function point(x, y) {
11519 switch (this._point) {
11522 this._context.moveTo(x, y);
11530 this._context.lineTo(x, y);
11537 this._context.moveTo(x + this._radius, y);
11539 this._context.arc(x, y, this._radius, 0, tau);
11548 var lengthSum$1 = new Adder(),
11554 var lengthStream$1 = {
11556 lineStart: function lineStart() {
11557 lengthStream$1.point = lengthPointFirst$1;
11559 lineEnd: function lineEnd() {
11560 if (lengthRing) lengthPoint$1(x00$2, y00$2);
11561 lengthStream$1.point = noop;
11563 polygonStart: function polygonStart() {
11566 polygonEnd: function polygonEnd() {
11569 result: function result() {
11570 var length = +lengthSum$1;
11571 lengthSum$1 = new Adder();
11576 function lengthPointFirst$1(x, y) {
11577 lengthStream$1.point = lengthPoint$1;
11578 x00$2 = x0$4 = x, y00$2 = y0$4 = y;
11581 function lengthPoint$1(x, y) {
11582 x0$4 -= x, y0$4 -= y;
11583 lengthSum$1.add(sqrt$1(x0$4 * x0$4 + y0$4 * y0$4));
11584 x0$4 = x, y0$4 = y;
11587 function PathString() {
11590 PathString.prototype = {
11592 _circle: circle(4.5),
11593 pointRadius: function pointRadius(_) {
11594 if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
11597 polygonStart: function polygonStart() {
11600 polygonEnd: function polygonEnd() {
11603 lineStart: function lineStart() {
11606 lineEnd: function lineEnd() {
11607 if (this._line === 0) this._string.push("Z");
11610 point: function point(x, y) {
11611 switch (this._point) {
11614 this._string.push("M", x, ",", y);
11622 this._string.push("L", x, ",", y);
11629 if (this._circle == null) this._circle = circle(this._radius);
11631 this._string.push("M", x, ",", y, this._circle);
11637 result: function result() {
11638 if (this._string.length) {
11639 var result = this._string.join("");
11649 function circle(radius) {
11650 return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
11653 function d3_geoPath (projection, context) {
11654 var pointRadius = 4.5,
11658 function path(object) {
11660 if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
11661 d3_geoStream(object, projectionStream(contextStream));
11664 return contextStream.result();
11667 path.area = function (object) {
11668 d3_geoStream(object, projectionStream(areaStream$1));
11669 return areaStream$1.result();
11672 path.measure = function (object) {
11673 d3_geoStream(object, projectionStream(lengthStream$1));
11674 return lengthStream$1.result();
11677 path.bounds = function (object) {
11678 d3_geoStream(object, projectionStream(boundsStream$1));
11679 return boundsStream$1.result();
11682 path.centroid = function (object) {
11683 d3_geoStream(object, projectionStream(centroidStream$1));
11684 return centroidStream$1.result();
11687 path.projection = function (_) {
11688 return arguments.length ? (projectionStream = _ == null ? (projection = null, identity) : (projection = _).stream, path) : projection;
11691 path.context = function (_) {
11692 if (!arguments.length) return context;
11693 contextStream = _ == null ? (context = null, new PathString()) : new PathContext(context = _);
11694 if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
11698 path.pointRadius = function (_) {
11699 if (!arguments.length) return pointRadius;
11700 pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
11704 return path.projection(projection).context(context);
11707 function d3_geoTransform (methods) {
11709 stream: transformer(methods)
11712 function transformer(methods) {
11713 return function (stream) {
11714 var s = new TransformStream();
11716 for (var key in methods) {
11717 s[key] = methods[key];
11725 function TransformStream() {}
11727 TransformStream.prototype = {
11728 constructor: TransformStream,
11729 point: function point(x, y) {
11730 this.stream.point(x, y);
11732 sphere: function sphere() {
11733 this.stream.sphere();
11735 lineStart: function lineStart() {
11736 this.stream.lineStart();
11738 lineEnd: function lineEnd() {
11739 this.stream.lineEnd();
11741 polygonStart: function polygonStart() {
11742 this.stream.polygonStart();
11744 polygonEnd: function polygonEnd() {
11745 this.stream.polygonEnd();
11749 function fit(projection, fitBounds, object) {
11750 var clip = projection.clipExtent && projection.clipExtent();
11751 projection.scale(150).translate([0, 0]);
11752 if (clip != null) projection.clipExtent(null);
11753 d3_geoStream(object, projection.stream(boundsStream$1));
11754 fitBounds(boundsStream$1.result());
11755 if (clip != null) projection.clipExtent(clip);
11759 function fitExtent(projection, extent, object) {
11760 return fit(projection, function (b) {
11761 var w = extent[1][0] - extent[0][0],
11762 h = extent[1][1] - extent[0][1],
11763 k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
11764 x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
11765 y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
11766 projection.scale(150 * k).translate([x, y]);
11769 function fitSize(projection, size, object) {
11770 return fitExtent(projection, [[0, 0], size], object);
11772 function fitWidth(projection, width, object) {
11773 return fit(projection, function (b) {
11775 k = w / (b[1][0] - b[0][0]),
11776 x = (w - k * (b[1][0] + b[0][0])) / 2,
11778 projection.scale(150 * k).translate([x, y]);
11781 function fitHeight(projection, height, object) {
11782 return fit(projection, function (b) {
11784 k = h / (b[1][1] - b[0][1]),
11786 y = (h - k * (b[1][1] + b[0][1])) / 2;
11787 projection.scale(150 * k).translate([x, y]);
11792 // maximum depth of subdivision
11793 cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
11795 function resample (project, delta2) {
11796 return +delta2 ? resample$1(project, delta2) : resampleNone(project);
11799 function resampleNone(project) {
11800 return transformer({
11801 point: function point(x, y) {
11803 this.stream.point(x[0], x[1]);
11808 function resample$1(project, delta2) {
11809 function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
11812 d2 = dx * dx + dy * dy;
11814 if (d2 > 4 * delta2 && depth--) {
11818 m = sqrt$1(a * a + b * b + c * c),
11819 phi2 = asin(c /= m),
11820 lambda2 = abs$2(abs$2(c) - 1) < epsilon || abs$2(lambda0 - lambda1) < epsilon ? (lambda0 + lambda1) / 2 : atan2(b, a),
11821 p = project(lambda2, phi2),
11826 dz = dy * dx2 - dx * dy2;
11828 if (dz * dz / d2 > delta2 // perpendicular projected distance
11829 || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
11830 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
11831 // angular distance
11832 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
11833 stream.point(x2, y2);
11834 resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
11839 return function (stream) {
11840 var lambda00, x00, y00, a00, b00, c00, // first point
11841 lambda0, x0, y0, a0, b0, c0; // previous point
11843 var resampleStream = {
11845 lineStart: lineStart,
11847 polygonStart: function polygonStart() {
11848 stream.polygonStart();
11849 resampleStream.lineStart = ringStart;
11851 polygonEnd: function polygonEnd() {
11852 stream.polygonEnd();
11853 resampleStream.lineStart = lineStart;
11857 function point(x, y) {
11859 stream.point(x[0], x[1]);
11862 function lineStart() {
11864 resampleStream.point = linePoint;
11865 stream.lineStart();
11868 function linePoint(lambda, phi) {
11869 var c = cartesian([lambda, phi]),
11870 p = project(lambda, phi);
11871 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);
11872 stream.point(x0, y0);
11875 function lineEnd() {
11876 resampleStream.point = point;
11880 function ringStart() {
11882 resampleStream.point = ringPoint;
11883 resampleStream.lineEnd = ringEnd;
11886 function ringPoint(lambda, phi) {
11887 linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
11888 resampleStream.point = linePoint;
11891 function ringEnd() {
11892 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
11893 resampleStream.lineEnd = lineEnd;
11897 return resampleStream;
11901 var transformRadians = transformer({
11902 point: function point(x, y) {
11903 this.stream.point(x * radians, y * radians);
11907 function transformRotate(rotate) {
11908 return transformer({
11909 point: function point(x, y) {
11910 var r = rotate(x, y);
11911 return this.stream.point(r[0], r[1]);
11916 function scaleTranslate(k, dx, dy, sx, sy) {
11917 function transform(x, y) {
11920 return [dx + k * x, dy - k * y];
11923 transform.invert = function (x, y) {
11924 return [(x - dx) / k * sx, (dy - y) / k * sy];
11930 function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
11931 if (!alpha) return scaleTranslate(k, dx, dy, sx, sy);
11932 var cosAlpha = cos(alpha),
11933 sinAlpha = sin(alpha),
11938 ci = (sinAlpha * dy - cosAlpha * dx) / k,
11939 fi = (sinAlpha * dx + cosAlpha * dy) / k;
11941 function transform(x, y) {
11944 return [a * x - b * y + dx, dy - b * x - a * y];
11947 transform.invert = function (x, y) {
11948 return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
11954 function projection(project) {
11955 return projectionMutator(function () {
11959 function projectionMutator(projectAt) {
11975 // post-rotate angle
11981 preclip = clipAntimeridian,
11987 postclip = identity,
11988 // post-clip extent
11993 projectRotateTransform,
11997 function projection(point) {
11998 return projectRotateTransform(point[0] * radians, point[1] * radians);
12001 function invert(point) {
12002 point = projectRotateTransform.invert(point[0], point[1]);
12003 return point && [point[0] * degrees, point[1] * degrees];
12006 projection.stream = function (stream) {
12007 return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
12010 projection.preclip = function (_) {
12011 return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
12014 projection.postclip = function (_) {
12015 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12018 projection.clipAngle = function (_) {
12019 return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees;
12022 projection.clipExtent = function (_) {
12023 return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12026 projection.scale = function (_) {
12027 return arguments.length ? (k = +_, recenter()) : k;
12030 projection.translate = function (_) {
12031 return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
12034 projection.center = function (_) {
12035 return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees, phi * degrees];
12038 projection.rotate = function (_) {
12039 return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees, deltaPhi * degrees, deltaGamma * degrees];
12042 projection.angle = function (_) {
12043 return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees;
12046 projection.reflectX = function (_) {
12047 return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
12050 projection.reflectY = function (_) {
12051 return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
12054 projection.precision = function (_) {
12055 return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt$1(delta2);
12058 projection.fitExtent = function (extent, object) {
12059 return fitExtent(projection, extent, object);
12062 projection.fitSize = function (size, object) {
12063 return fitSize(projection, size, object);
12066 projection.fitWidth = function (width, object) {
12067 return fitWidth(projection, width, object);
12070 projection.fitHeight = function (height, object) {
12071 return fitHeight(projection, height, object);
12074 function recenter() {
12075 var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
12076 transform = scaleTranslateRotate(k, x - center[0], y - center[1], sx, sy, alpha);
12077 rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
12078 projectTransform = compose(project, transform);
12079 projectRotateTransform = compose(rotate, projectTransform);
12080 projectResample = resample(projectTransform, delta2);
12085 cache = cacheStream = null;
12089 return function () {
12090 project = projectAt.apply(this, arguments);
12091 projection.invert = project.invert && invert;
12096 function mercatorRaw(lambda, phi) {
12097 return [lambda, log$1(tan((halfPi + phi) / 2))];
12100 mercatorRaw.invert = function (x, y) {
12101 return [x, 2 * atan(exp(y)) - halfPi];
12104 function mercator () {
12105 return mercatorProjection(mercatorRaw).scale(961 / tau);
12107 function mercatorProjection(project) {
12108 var m = projection(project),
12111 translate = m.translate,
12112 clipExtent = m.clipExtent,
12118 m.scale = function (_) {
12119 return arguments.length ? (scale(_), reclip()) : scale();
12122 m.translate = function (_) {
12123 return arguments.length ? (translate(_), reclip()) : translate();
12126 m.center = function (_) {
12127 return arguments.length ? (center(_), reclip()) : center();
12130 m.clipExtent = function (_) {
12131 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]];
12134 function reclip() {
12135 var k = pi * scale(),
12136 t = m(rotation(m.rotate()).invert([0, 0]));
12137 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)]]);
12143 function d3_geoIdentity () {
12149 // scale, translate and reflect
12161 transform = transformer({
12162 point: function point(x, y) {
12163 var p = projection([x, y]);
12164 this.stream.point(p[0], p[1]);
12167 postclip = identity,
12174 cache = cacheStream = null;
12178 function projection(p) {
12183 var t = y * ca - x * sa;
12184 x = x * ca + y * sa;
12188 return [x + tx, y + ty];
12191 projection.invert = function (p) {
12196 var t = y * ca + x * sa;
12197 x = x * ca - y * sa;
12201 return [x / kx, y / ky];
12204 projection.stream = function (stream) {
12205 return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
12208 projection.postclip = function (_) {
12209 return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
12212 projection.clipExtent = function (_) {
12213 return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
12216 projection.scale = function (_) {
12217 return arguments.length ? (k = +_, reset()) : k;
12220 projection.translate = function (_) {
12221 return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
12224 projection.angle = function (_) {
12225 return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees;
12228 projection.reflectX = function (_) {
12229 return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
12232 projection.reflectY = function (_) {
12233 return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
12236 projection.fitExtent = function (extent, object) {
12237 return fitExtent(projection, extent, object);
12240 projection.fitSize = function (size, object) {
12241 return fitSize(projection, size, object);
12244 projection.fitWidth = function (width, object) {
12245 return fitWidth(projection, width, object);
12248 projection.fitHeight = function (height, object) {
12249 return fitHeight(projection, height, object);
12256 var TAU = 2 * Math.PI;
12257 var EQUATORIAL_RADIUS = 6356752.314245179;
12258 var POLAR_RADIUS = 6378137.0;
12259 function geoLatToMeters(dLat) {
12260 return dLat * (TAU * POLAR_RADIUS / 360);
12262 function geoLonToMeters(dLon, atLat) {
12263 return Math.abs(atLat) >= 90 ? 0 : dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
12265 function geoMetersToLat(m) {
12266 return m / (TAU * POLAR_RADIUS / 360);
12268 function geoMetersToLon(m, atLat) {
12269 return Math.abs(atLat) >= 90 ? 0 : m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
12271 function geoMetersToOffset(meters, tileSize) {
12272 tileSize = tileSize || 256;
12273 return [meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS), -meters[1] * tileSize / (TAU * POLAR_RADIUS)];
12275 function geoOffsetToMeters(offset, tileSize) {
12276 tileSize = tileSize || 256;
12277 return [offset[0] * TAU * EQUATORIAL_RADIUS / tileSize, -offset[1] * TAU * POLAR_RADIUS / tileSize];
12278 } // Equirectangular approximation of spherical distances on Earth
12280 function geoSphericalDistance(a, b) {
12281 var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
12282 var y = geoLatToMeters(a[1] - b[1]);
12283 return Math.sqrt(x * x + y * y);
12286 function geoScaleToZoom(k, tileSize) {
12287 tileSize = tileSize || 256;
12288 var log2ts = Math.log(tileSize) * Math.LOG2E;
12289 return Math.log(k * TAU) / Math.LN2 - log2ts;
12292 function geoZoomToScale(z, tileSize) {
12293 tileSize = tileSize || 256;
12294 return tileSize * Math.pow(2, z) / TAU;
12295 } // returns info about the node from `nodes` closest to the given `point`
12297 function geoSphericalClosestNode(nodes, point) {
12298 var minDistance = Infinity,
12302 for (var i in nodes) {
12303 distance = geoSphericalDistance(nodes[i].loc, point);
12305 if (distance < minDistance) {
12306 minDistance = distance;
12311 if (indexOfMin !== undefined) {
12314 distance: minDistance,
12315 node: nodes[indexOfMin]
12322 function geoExtent(min, max) {
12323 if (!(this instanceof geoExtent)) {
12324 return new geoExtent(min, max);
12325 } else if (min instanceof geoExtent) {
12327 } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
12331 this[0] = min || [Infinity, Infinity];
12332 this[1] = max || min || [-Infinity, -Infinity];
12335 geoExtent.prototype = new Array(2);
12336 Object.assign(geoExtent.prototype, {
12337 equals: function equals(obj) {
12338 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];
12340 extend: function extend(obj) {
12341 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12342 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])]);
12344 _extend: function _extend(extent) {
12345 this[0][0] = Math.min(extent[0][0], this[0][0]);
12346 this[0][1] = Math.min(extent[0][1], this[0][1]);
12347 this[1][0] = Math.max(extent[1][0], this[1][0]);
12348 this[1][1] = Math.max(extent[1][1], this[1][1]);
12350 area: function area() {
12351 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
12353 center: function center() {
12354 return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2];
12356 rectangle: function rectangle() {
12357 return [this[0][0], this[0][1], this[1][0], this[1][1]];
12359 bbox: function bbox() {
12367 polygon: function polygon() {
12368 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]]];
12370 contains: function contains(obj) {
12371 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12372 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];
12374 intersects: function intersects(obj) {
12375 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12376 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];
12378 intersection: function intersection(obj) {
12379 if (!this.intersects(obj)) return new geoExtent();
12380 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])]);
12382 percentContainedIn: function percentContainedIn(obj) {
12383 if (!(obj instanceof geoExtent)) obj = new geoExtent(obj);
12384 var a1 = this.intersection(obj).area();
12385 var a2 = this.area();
12387 if (a1 === Infinity || a2 === Infinity) {
12389 } else if (a1 === 0 || a2 === 0) {
12390 if (obj.contains(this)) {
12399 padByMeters: function padByMeters(meters) {
12400 var dLat = geoMetersToLat(meters);
12401 var dLon = geoMetersToLon(meters, this.center()[1]);
12402 return geoExtent([this[0][0] - dLon, this[0][1] - dLat], [this[1][0] + dLon, this[1][1] + dLat]);
12404 toParam: function toParam() {
12405 return this.rectangle().join(',');
12409 var $every$1 = arrayIteration.every;
12413 var STRICT_METHOD$6 = arrayMethodIsStrict('every');
12414 var USES_TO_LENGTH$8 = arrayMethodUsesToLength('every');
12416 // `Array.prototype.every` method
12417 // https://tc39.github.io/ecma262/#sec-array.prototype.every
12418 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$6 || !USES_TO_LENGTH$8 }, {
12419 every: function every(callbackfn /* , thisArg */) {
12420 return $every$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
12424 var $reduce$1 = arrayReduce.left;
12428 var STRICT_METHOD$7 = arrayMethodIsStrict('reduce');
12429 var USES_TO_LENGTH$9 = arrayMethodUsesToLength('reduce', { 1: 0 });
12431 // `Array.prototype.reduce` method
12432 // https://tc39.github.io/ecma262/#sec-array.prototype.reduce
12433 _export({ target: 'Array', proto: true, forced: !STRICT_METHOD$7 || !USES_TO_LENGTH$9 }, {
12434 reduce: function reduce(callbackfn /* , initialValue */) {
12435 return $reduce$1(this, callbackfn, arguments.length, arguments.length > 1 ? arguments[1] : undefined);
12439 function d3_polygonArea (polygon) {
12441 n = polygon.length,
12443 b = polygon[n - 1],
12449 area += a[1] * b[0] - a[0] * b[1];
12455 function d3_polygonCentroid (polygon) {
12457 n = polygon.length,
12461 b = polygon[n - 1],
12468 k += c = a[0] * b[1] - b[0] * a[1];
12469 x += (a[0] + b[0]) * c;
12470 y += (a[1] + b[1]) * c;
12473 return k *= 3, [x / k, y / k];
12476 // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
12477 // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
12478 // right, +y is up). Returns a positive value if ABC is counter-clockwise,
12479 // negative if clockwise, and zero if the points are collinear.
12480 function cross (a, b, c) {
12481 return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
12484 function lexicographicOrder(a, b) {
12485 return a[0] - b[0] || a[1] - b[1];
12486 } // Computes the upper convex hull per the monotone chain algorithm.
12487 // Assumes points.length >= 3, is sorted by x, unique in y.
12488 // Returns an array of indices into points in left-to-right order.
12491 function computeUpperHullIndexes(points) {
12492 var n = points.length,
12497 for (i = 2; i < n; ++i) {
12498 while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) {
12502 indexes[size++] = i;
12505 return indexes.slice(0, size); // remove popped points
12508 function d3_polygonHull (points) {
12509 if ((n = points.length) < 3) return null;
12512 sortedPoints = new Array(n),
12513 flippedPoints = new Array(n);
12515 for (i = 0; i < n; ++i) {
12516 sortedPoints[i] = [+points[i][0], +points[i][1], i];
12519 sortedPoints.sort(lexicographicOrder);
12521 for (i = 0; i < n; ++i) {
12522 flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
12525 var upperIndexes = computeUpperHullIndexes(sortedPoints),
12526 lowerIndexes = computeUpperHullIndexes(flippedPoints); // Construct the hull polygon, removing possible duplicate endpoints.
12528 var skipLeft = lowerIndexes[0] === upperIndexes[0],
12529 skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
12530 hull = []; // Add upper hull in right-to-l order.
12531 // Then add lower hull in left-to-right order.
12533 for (i = upperIndexes.length - 1; i >= 0; --i) {
12534 hull.push(points[sortedPoints[upperIndexes[i]][2]]);
12537 for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) {
12538 hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
12545 function geoVecEqual(a, b, epsilon) {
12547 return Math.abs(a[0] - b[0]) <= epsilon && Math.abs(a[1] - b[1]) <= epsilon;
12549 return a[0] === b[0] && a[1] === b[1];
12551 } // vector addition
12553 function geoVecAdd(a, b) {
12554 return [a[0] + b[0], a[1] + b[1]];
12555 } // vector subtraction
12557 function geoVecSubtract(a, b) {
12558 return [a[0] - b[0], a[1] - b[1]];
12559 } // vector scaling
12561 function geoVecScale(a, mag) {
12562 return [a[0] * mag, a[1] * mag];
12563 } // vector rounding (was: geoRoundCoordinates)
12565 function geoVecFloor(a) {
12566 return [Math.floor(a[0]), Math.floor(a[1])];
12567 } // linear interpolation
12569 function geoVecInterp(a, b, t) {
12570 return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
12571 } // http://jsperf.com/id-dist-optimization
12573 function geoVecLength(a, b) {
12574 return Math.sqrt(geoVecLengthSquare(a, b));
12575 } // length of vector raised to the power two
12577 function geoVecLengthSquare(a, b) {
12579 var x = a[0] - b[0];
12580 var y = a[1] - b[1];
12581 return x * x + y * y;
12582 } // get a unit vector
12584 function geoVecNormalize(a) {
12585 var length = Math.sqrt(a[0] * a[0] + a[1] * a[1]);
12587 if (length !== 0) {
12588 return geoVecScale(a, 1 / length);
12592 } // Return the counterclockwise angle in the range (-pi, pi)
12593 // between the positive X axis and the line intersecting a and b.
12595 function geoVecAngle(a, b) {
12596 return Math.atan2(b[1] - a[1], b[0] - a[0]);
12599 function geoVecDot(a, b, origin) {
12600 origin = origin || [0, 0];
12601 var p = geoVecSubtract(a, origin);
12602 var q = geoVecSubtract(b, origin);
12603 return p[0] * q[0] + p[1] * q[1];
12604 } // normalized dot product
12606 function geoVecNormalizedDot(a, b, origin) {
12607 origin = origin || [0, 0];
12608 var p = geoVecNormalize(geoVecSubtract(a, origin));
12609 var q = geoVecNormalize(geoVecSubtract(b, origin));
12610 return geoVecDot(p, q);
12611 } // 2D cross product of OA and OB vectors, returns magnitude of Z vector
12612 // Returns a positive value, if OAB makes a counter-clockwise turn,
12613 // negative for clockwise turn, and zero if the points are collinear.
12615 function geoVecCross(a, b, origin) {
12616 origin = origin || [0, 0];
12617 var p = geoVecSubtract(a, origin);
12618 var q = geoVecSubtract(b, origin);
12619 return p[0] * q[1] - p[1] * q[0];
12620 } // find closest orthogonal projection of point onto points array
12622 function geoVecProject(a, points) {
12623 var min = Infinity;
12627 for (var i = 0; i < points.length - 1; i++) {
12629 var s = geoVecSubtract(points[i + 1], o);
12630 var v = geoVecSubtract(a, o);
12631 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12636 } else if (proj > 1) {
12639 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12642 var dist = geoVecLength(p, a);
12651 if (idx !== undefined) {
12662 // between the positive X axis and the line intersecting a and b.
12664 function geoAngle(a, b, projection) {
12665 return geoVecAngle(projection(a.loc), projection(b.loc));
12667 function geoEdgeEqual(a, b) {
12668 return a[0] === b[0] && a[1] === b[1] || a[0] === b[1] && a[1] === b[0];
12669 } // Rotate all points counterclockwise around a pivot point by given angle
12671 function geoRotate(points, angle, around) {
12672 return points.map(function (point) {
12673 var radial = geoVecSubtract(point, around);
12674 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]];
12676 } // Choose the edge with the minimal distance from `point` to its orthogonal
12677 // projection onto that edge, if such a projection exists, or the distance to
12678 // the closest vertex on that edge. Returns an object with the `index` of the
12679 // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
12681 function geoChooseEdge(nodes, point, projection, activeID) {
12682 var dist = geoVecLength;
12683 var points = nodes.map(function (n) {
12684 return projection(n.loc);
12686 var ids = nodes.map(function (n) {
12689 var min = Infinity;
12693 for (var i = 0; i < points.length - 1; i++) {
12694 if (ids[i] === activeID || ids[i + 1] === activeID) continue;
12696 var s = geoVecSubtract(points[i + 1], o);
12697 var v = geoVecSubtract(point, o);
12698 var proj = geoVecDot(v, s) / geoVecDot(s, s);
12703 } else if (proj > 1) {
12706 p = [o[0] + proj * s[0], o[1] + proj * s[1]];
12709 var d = dist(p, point);
12714 loc = projection.invert(p);
12718 if (idx !== undefined) {
12727 } // Test active (dragged or drawing) segments against inactive segments
12728 // This is used to test e.g. multipolygon rings that cross
12729 // `activeNodes` is the ring containing the activeID being dragged.
12730 // `inactiveNodes` is the other ring to test against
12732 function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
12734 var inactives = [];
12735 var j, k, n1, n2, segment; // gather active segments (only segments in activeNodes that contain the activeID)
12737 for (j = 0; j < activeNodes.length - 1; j++) {
12738 n1 = activeNodes[j];
12739 n2 = activeNodes[j + 1];
12740 segment = [n1.loc, n2.loc];
12742 if (n1.id === activeID || n2.id === activeID) {
12743 actives.push(segment);
12745 } // gather inactive segments
12748 for (j = 0; j < inactiveNodes.length - 1; j++) {
12749 n1 = inactiveNodes[j];
12750 n2 = inactiveNodes[j + 1];
12751 segment = [n1.loc, n2.loc];
12752 inactives.push(segment);
12756 for (j = 0; j < actives.length; j++) {
12757 for (k = 0; k < inactives.length; k++) {
12758 var p = actives[j];
12759 var q = inactives[k];
12760 var hit = geoLineIntersection(p, q);
12769 } // Test active (dragged or drawing) segments against inactive segments
12770 // This is used to test whether a way intersects with itself.
12772 function geoHasSelfIntersections(nodes, activeID) {
12774 var inactives = [];
12775 var j, k; // group active and passive segments along the nodes
12777 for (j = 0; j < nodes.length - 1; j++) {
12779 var n2 = nodes[j + 1];
12780 var segment = [n1.loc, n2.loc];
12782 if (n1.id === activeID || n2.id === activeID) {
12783 actives.push(segment);
12785 inactives.push(segment);
12790 for (j = 0; j < actives.length; j++) {
12791 for (k = 0; k < inactives.length; k++) {
12792 var p = actives[j];
12793 var q = inactives[k]; // skip if segments share an endpoint
12795 if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) || geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1])) {
12799 var hit = geoLineIntersection(p, q);
12802 var epsilon = 1e-8; // skip if the hit is at the segment's endpoint
12804 if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) || geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon)) {
12814 } // Return the intersection point of 2 line segments.
12815 // From https://github.com/pgkelley4/line-segments-intersect
12816 // This uses the vector cross product approach described below:
12817 // http://stackoverflow.com/a/565282/786339
12819 function geoLineIntersection(a, b) {
12820 var p = [a[0][0], a[0][1]];
12821 var p2 = [a[1][0], a[1][1]];
12822 var q = [b[0][0], b[0][1]];
12823 var q2 = [b[1][0], b[1][1]];
12824 var r = geoVecSubtract(p2, p);
12825 var s = geoVecSubtract(q2, q);
12826 var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
12827 var denominator = geoVecCross(r, s);
12829 if (uNumerator && denominator) {
12830 var u = uNumerator / denominator;
12831 var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
12833 if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
12834 return geoVecInterp(p, p2, t);
12840 function geoPathIntersections(path1, path2) {
12841 var intersections = [];
12843 for (var i = 0; i < path1.length - 1; i++) {
12844 for (var j = 0; j < path2.length - 1; j++) {
12845 var a = [path1[i], path1[i + 1]];
12846 var b = [path2[j], path2[j + 1]];
12847 var hit = geoLineIntersection(a, b);
12850 intersections.push(hit);
12855 return intersections;
12857 function geoPathHasIntersections(path1, path2) {
12858 for (var i = 0; i < path1.length - 1; i++) {
12859 for (var j = 0; j < path2.length - 1; j++) {
12860 var a = [path1[i], path1[i + 1]];
12861 var b = [path2[j], path2[j + 1]];
12862 var hit = geoLineIntersection(a, b);
12871 } // Return whether point is contained in polygon.
12873 // `point` should be a 2-item array of coordinates.
12874 // `polygon` should be an array of 2-item arrays of coordinates.
12876 // From https://github.com/substack/point-in-polygon.
12877 // ray-casting algorithm based on
12878 // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
12881 function geoPointInPolygon(point, polygon) {
12884 var inside = false;
12886 for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
12887 var xi = polygon[i][0];
12888 var yi = polygon[i][1];
12889 var xj = polygon[j][0];
12890 var yj = polygon[j][1];
12891 var intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
12892 if (intersect) inside = !inside;
12897 function geoPolygonContainsPolygon(outer, inner) {
12898 return inner.every(function (point) {
12899 return geoPointInPolygon(point, outer);
12902 function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
12903 function testPoints(outer, inner) {
12904 return inner.some(function (point) {
12905 return geoPointInPolygon(point, outer);
12909 return testPoints(outer, inner) || !!checkSegments && geoPathHasIntersections(outer, inner);
12910 } // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
12911 // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
12913 function geoGetSmallestSurroundingRectangle(points) {
12914 var hull = d3_polygonHull(points);
12915 var centroid = d3_polygonCentroid(hull);
12916 var minArea = Infinity;
12917 var ssrExtent = [];
12921 for (var i = 0; i <= hull.length - 1; i++) {
12922 var c2 = i === hull.length - 1 ? hull[0] : hull[i + 1];
12923 var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
12924 var poly = geoRotate(hull, -angle, centroid);
12925 var extent = poly.reduce(function (extent, point) {
12926 return extent.extend(geoExtent(point));
12928 var area = extent.area();
12930 if (area < minArea) {
12932 ssrExtent = extent;
12940 poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
12944 function geoPathLength(path) {
12947 for (var i = 0; i < path.length - 1; i++) {
12948 length += geoVecLength(path[i], path[i + 1]);
12952 } // If the given point is at the edge of the padded viewport,
12953 // return a vector that will nudge the viewport in that direction
12955 function geoViewportEdge(point, dimensions) {
12956 var pad = [80, 20, 50, 20]; // top, right, bottom, left
12960 if (point[0] > dimensions[0] - pad[1]) x = -10;
12961 if (point[0] < pad[3]) x = 10;
12962 if (point[1] > dimensions[1] - pad[2]) y = -10;
12963 if (point[1] < pad[0]) y = 10;
12973 value: function value() {}
12976 function dispatch() {
12977 for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
12978 if (!(t = arguments[i] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
12982 return new Dispatch$1(_);
12985 function Dispatch$1(_) {
12989 function parseTypenames(typenames, types) {
12990 return typenames.trim().split(/^|\s+/).map(function (t) {
12992 i = t.indexOf(".");
12993 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
12994 if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
13002 Dispatch$1.prototype = dispatch.prototype = {
13003 constructor: Dispatch$1,
13004 on: function on(typename, callback) {
13006 T = parseTypenames(typename + "", _),
13009 n = T.length; // If no callback was specified, return the callback of the given type and name.
13011 if (arguments.length < 2) {
13013 if ((t = (typename = T[i]).type) && (t = get$3(_[t], typename.name))) return t;
13017 } // If a type was specified, set the callback for the given type and name.
13018 // Otherwise, if a null callback was specified, remove callbacks of the given name.
13021 if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
13024 if (t = (typename = T[i]).type) _[t] = set$3(_[t], typename.name, callback);else if (callback == null) for (t in _) {
13025 _[t] = set$3(_[t], typename.name, null);
13031 copy: function copy() {
13036 copy[t] = _[t].slice();
13039 return new Dispatch$1(copy);
13041 call: function call(type, that) {
13042 if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) {
13043 args[i] = arguments[i + 2];
13045 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13047 for (t = this._[type], i = 0, n = t.length; i < n; ++i) {
13048 t[i].value.apply(that, args);
13051 apply: function apply(type, that, args) {
13052 if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
13054 for (var t = this._[type], i = 0, n = t.length; i < n; ++i) {
13055 t[i].value.apply(that, args);
13060 function get$3(type, name) {
13061 for (var i = 0, n = type.length, c; i < n; ++i) {
13062 if ((c = type[i]).name === name) {
13068 function set$3(type, name, callback) {
13069 for (var i = 0, n = type.length; i < n; ++i) {
13070 if (type[i].name === name) {
13071 type[i] = noop$1, type = type.slice(0, i).concat(type.slice(i + 1));
13076 if (callback != null) type.push({
13083 var xhtml = "http://www.w3.org/1999/xhtml";
13085 svg: "http://www.w3.org/2000/svg",
13087 xlink: "http://www.w3.org/1999/xlink",
13088 xml: "http://www.w3.org/XML/1998/namespace",
13089 xmlns: "http://www.w3.org/2000/xmlns/"
13092 function namespace (name) {
13093 var prefix = name += "",
13094 i = prefix.indexOf(":");
13095 if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
13096 return namespaces.hasOwnProperty(prefix) ? {
13097 space: namespaces[prefix],
13099 } : name; // eslint-disable-line no-prototype-builtins
13102 function creatorInherit(name) {
13103 return function () {
13104 var document = this.ownerDocument,
13105 uri = this.namespaceURI;
13106 return uri === xhtml && document.documentElement.namespaceURI === xhtml ? document.createElement(name) : document.createElementNS(uri, name);
13110 function creatorFixed(fullname) {
13111 return function () {
13112 return this.ownerDocument.createElementNS(fullname.space, fullname.local);
13116 function creator (name) {
13117 var fullname = namespace(name);
13118 return (fullname.local ? creatorFixed : creatorInherit)(fullname);
13123 function selector (selector) {
13124 return selector == null ? none : function () {
13125 return this.querySelector(selector);
13129 function selection_select (select) {
13130 if (typeof select !== "function") select = selector(select);
13132 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13133 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
13134 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
13135 if ("__data__" in node) subnode.__data__ = node.__data__;
13136 subgroup[i] = subnode;
13141 return new Selection(subgroups, this._parents);
13144 function array (x) {
13145 return _typeof(x) === "object" && "length" in x ? x // Array, TypedArray, NodeList, array-like
13146 : Array.from(x); // Map, Set, iterable, string, or anything else
13153 function selectorAll (selector) {
13154 return selector == null ? empty : function () {
13155 return this.querySelectorAll(selector);
13159 function arrayAll(select) {
13160 return function () {
13161 var group = select.apply(this, arguments);
13162 return group == null ? [] : array(group);
13166 function selection_selectAll (select) {
13167 if (typeof select === "function") select = arrayAll(select);else select = selectorAll(select);
13169 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
13170 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
13171 if (node = group[i]) {
13172 subgroups.push(select.call(node, node.__data__, i, group));
13173 parents.push(node);
13178 return new Selection(subgroups, parents);
13181 var $find$1 = arrayIteration.find;
13186 var SKIPS_HOLES = true;
13188 var USES_TO_LENGTH$a = arrayMethodUsesToLength(FIND);
13190 // Shouldn't skip holes
13191 if (FIND in []) Array(1)[FIND](function () { SKIPS_HOLES = false; });
13193 // `Array.prototype.find` method
13194 // https://tc39.github.io/ecma262/#sec-array.prototype.find
13195 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES || !USES_TO_LENGTH$a }, {
13196 find: function find(callbackfn /* , that = undefined */) {
13197 return $find$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
13201 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
13202 addToUnscopables(FIND);
13204 function matcher (selector) {
13205 return function () {
13206 return this.matches(selector);
13209 function childMatcher(selector) {
13210 return function (node) {
13211 return node.matches(selector);
13215 var find$1 = Array.prototype.find;
13217 function childFind(match) {
13218 return function () {
13219 return find$1.call(this.children, match);
13223 function childFirst() {
13224 return this.firstElementChild;
13227 function selection_selectChild (match) {
13228 return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match)));
13231 var filter = Array.prototype.filter;
13233 function children() {
13234 return this.children;
13237 function childrenFilter(match) {
13238 return function () {
13239 return filter.call(this.children, match);
13243 function selection_selectChildren (match) {
13244 return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match)));
13247 function selection_filter (match) {
13248 if (typeof match !== "function") match = matcher(match);
13250 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
13251 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
13252 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
13253 subgroup.push(node);
13258 return new Selection(subgroups, this._parents);
13261 function sparse (update) {
13262 return new Array(update.length);
13265 function selection_enter () {
13266 return new Selection(this._enter || this._groups.map(sparse), this._parents);
13268 function EnterNode(parent, datum) {
13269 this.ownerDocument = parent.ownerDocument;
13270 this.namespaceURI = parent.namespaceURI;
13272 this._parent = parent;
13273 this.__data__ = datum;
13275 EnterNode.prototype = {
13276 constructor: EnterNode,
13277 appendChild: function appendChild(child) {
13278 return this._parent.insertBefore(child, this._next);
13280 insertBefore: function insertBefore(child, next) {
13281 return this._parent.insertBefore(child, next);
13283 querySelector: function querySelector(selector) {
13284 return this._parent.querySelector(selector);
13286 querySelectorAll: function querySelectorAll(selector) {
13287 return this._parent.querySelectorAll(selector);
13291 function constant (x) {
13292 return function () {
13297 function bindIndex(parent, group, enter, update, exit, data) {
13300 groupLength = group.length,
13301 dataLength = data.length; // Put any non-null nodes that fit into update.
13302 // Put any null nodes into enter.
13303 // Put any remaining data into enter.
13305 for (; i < dataLength; ++i) {
13306 if (node = group[i]) {
13307 node.__data__ = data[i];
13310 enter[i] = new EnterNode(parent, data[i]);
13312 } // Put any non-null nodes that don’t fit into exit.
13315 for (; i < groupLength; ++i) {
13316 if (node = group[i]) {
13322 function bindKey(parent, group, enter, update, exit, data, key) {
13325 nodeByKeyValue = new Map(),
13326 groupLength = group.length,
13327 dataLength = data.length,
13328 keyValues = new Array(groupLength),
13329 keyValue; // Compute the key for each node.
13330 // If multiple nodes have the same key, the duplicates are added to exit.
13332 for (i = 0; i < groupLength; ++i) {
13333 if (node = group[i]) {
13334 keyValues[i] = keyValue = key.call(node, node.__data__, i, group) + "";
13336 if (nodeByKeyValue.has(keyValue)) {
13339 nodeByKeyValue.set(keyValue, node);
13342 } // Compute the key for each datum.
13343 // If there a node associated with this key, join and add it to update.
13344 // If there is not (or the key is a duplicate), add it to enter.
13347 for (i = 0; i < dataLength; ++i) {
13348 keyValue = key.call(parent, data[i], i, data) + "";
13350 if (node = nodeByKeyValue.get(keyValue)) {
13352 node.__data__ = data[i];
13353 nodeByKeyValue["delete"](keyValue);
13355 enter[i] = new EnterNode(parent, data[i]);
13357 } // Add any remaining nodes that were not bound to data to exit.
13360 for (i = 0; i < groupLength; ++i) {
13361 if ((node = group[i]) && nodeByKeyValue.get(keyValues[i]) === node) {
13367 function datum(node) {
13368 return node.__data__;
13371 function selection_data (value, key) {
13372 if (!arguments.length) return Array.from(this, datum);
13373 var bind = key ? bindKey : bindIndex,
13374 parents = this._parents,
13375 groups = this._groups;
13376 if (typeof value !== "function") value = constant(value);
13378 for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
13379 var parent = parents[j],
13381 groupLength = group.length,
13382 data = array(value.call(parent, parent && parent.__data__, j, parents)),
13383 dataLength = data.length,
13384 enterGroup = enter[j] = new Array(dataLength),
13385 updateGroup = update[j] = new Array(dataLength),
13386 exitGroup = exit[j] = new Array(groupLength);
13387 bind(parent, group, enterGroup, updateGroup, exitGroup, data, key); // Now connect the enter nodes to their following update node, such that
13388 // appendChild can insert the materialized enter node before this node,
13389 // rather than at the end of the parent node.
13391 for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
13392 if (previous = enterGroup[i0]) {
13393 if (i0 >= i1) i1 = i0 + 1;
13395 while (!(next = updateGroup[i1]) && ++i1 < dataLength) {
13398 previous._next = next || null;
13403 update = new Selection(update, parents);
13404 update._enter = enter;
13405 update._exit = exit;
13409 function selection_exit () {
13410 return new Selection(this._exit || this._groups.map(sparse), this._parents);
13413 function selection_join (onenter, onupdate, onexit) {
13414 var enter = this.enter(),
13416 exit = this.exit();
13417 enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
13418 if (onupdate != null) update = onupdate(update);
13419 if (onexit == null) exit.remove();else onexit(exit);
13420 return enter && update ? enter.merge(update).order() : update;
13423 function selection_merge (selection) {
13424 if (!(selection instanceof Selection)) throw new Error("invalid merge");
13426 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) {
13427 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
13428 if (node = group0[i] || group1[i]) {
13434 for (; j < m0; ++j) {
13435 merges[j] = groups0[j];
13438 return new Selection(merges, this._parents);
13441 function selection_order () {
13442 for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
13443 for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
13444 if (node = group[i]) {
13445 if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
13454 function selection_sort (compare) {
13455 if (!compare) compare = ascending;
13457 function compareNode(a, b) {
13458 return a && b ? compare(a.__data__, b.__data__) : !a - !b;
13461 for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
13462 for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
13463 if (node = group[i]) {
13464 sortgroup[i] = node;
13468 sortgroup.sort(compareNode);
13471 return new Selection(sortgroups, this._parents).order();
13474 function ascending(a, b) {
13475 return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
13478 function selection_call () {
13479 var callback = arguments[0];
13480 arguments[0] = this;
13481 callback.apply(null, arguments);
13485 function selection_nodes () {
13486 return Array.from(this);
13489 function selection_node () {
13490 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13491 for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
13492 var node = group[i];
13493 if (node) return node;
13500 function selection_size () {
13503 var _iterator = _createForOfIteratorHelper(this),
13507 for (_iterator.s(); !(_step = _iterator.n()).done;) {
13508 var node = _step.value;
13510 } // eslint-disable-line no-unused-vars
13521 function selection_empty () {
13522 return !this.node();
13525 function selection_each (callback) {
13526 for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
13527 for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
13528 if (node = group[i]) callback.call(node, node.__data__, i, group);
13535 function attrRemove(name) {
13536 return function () {
13537 this.removeAttribute(name);
13541 function attrRemoveNS(fullname) {
13542 return function () {
13543 this.removeAttributeNS(fullname.space, fullname.local);
13547 function attrConstant(name, value) {
13548 return function () {
13549 this.setAttribute(name, value);
13553 function attrConstantNS(fullname, value) {
13554 return function () {
13555 this.setAttributeNS(fullname.space, fullname.local, value);
13559 function attrFunction(name, value) {
13560 return function () {
13561 var v = value.apply(this, arguments);
13562 if (v == null) this.removeAttribute(name);else this.setAttribute(name, v);
13566 function attrFunctionNS(fullname, value) {
13567 return function () {
13568 var v = value.apply(this, arguments);
13569 if (v == null) this.removeAttributeNS(fullname.space, fullname.local);else this.setAttributeNS(fullname.space, fullname.local, v);
13573 function selection_attr (name, value) {
13574 var fullname = namespace(name);
13576 if (arguments.length < 2) {
13577 var node = this.node();
13578 return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
13581 return this.each((value == null ? fullname.local ? attrRemoveNS : attrRemove : typeof value === "function" ? fullname.local ? attrFunctionNS : attrFunction : fullname.local ? attrConstantNS : attrConstant)(fullname, value));
13584 function defaultView (node) {
13585 return node.ownerDocument && node.ownerDocument.defaultView || // node is a Node
13586 node.document && node // node is a Window
13587 || node.defaultView; // node is a Document
13590 function styleRemove(name) {
13591 return function () {
13592 this.style.removeProperty(name);
13596 function styleConstant(name, value, priority) {
13597 return function () {
13598 this.style.setProperty(name, value, priority);
13602 function styleFunction(name, value, priority) {
13603 return function () {
13604 var v = value.apply(this, arguments);
13605 if (v == null) this.style.removeProperty(name);else this.style.setProperty(name, v, priority);
13609 function selection_style (name, value, priority) {
13610 return arguments.length > 1 ? this.each((value == null ? styleRemove : typeof value === "function" ? styleFunction : styleConstant)(name, value, priority == null ? "" : priority)) : styleValue(this.node(), name);
13612 function styleValue(node, name) {
13613 return node.style.getPropertyValue(name) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
13616 function propertyRemove(name) {
13617 return function () {
13622 function propertyConstant(name, value) {
13623 return function () {
13624 this[name] = value;
13628 function propertyFunction(name, value) {
13629 return function () {
13630 var v = value.apply(this, arguments);
13631 if (v == null) delete this[name];else this[name] = v;
13635 function selection_property (name, value) {
13636 return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name, value)) : this.node()[name];
13639 function classArray(string) {
13640 return string.trim().split(/^|\s+/);
13643 function classList(node) {
13644 return node.classList || new ClassList(node);
13647 function ClassList(node) {
13649 this._names = classArray(node.getAttribute("class") || "");
13652 ClassList.prototype = {
13653 add: function add(name) {
13654 var i = this._names.indexOf(name);
13657 this._names.push(name);
13659 this._node.setAttribute("class", this._names.join(" "));
13662 remove: function remove(name) {
13663 var i = this._names.indexOf(name);
13666 this._names.splice(i, 1);
13668 this._node.setAttribute("class", this._names.join(" "));
13671 contains: function contains(name) {
13672 return this._names.indexOf(name) >= 0;
13676 function classedAdd(node, names) {
13677 var list = classList(node),
13682 list.add(names[i]);
13686 function classedRemove(node, names) {
13687 var list = classList(node),
13692 list.remove(names[i]);
13696 function classedTrue(names) {
13697 return function () {
13698 classedAdd(this, names);
13702 function classedFalse(names) {
13703 return function () {
13704 classedRemove(this, names);
13708 function classedFunction(names, value) {
13709 return function () {
13710 (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
13714 function selection_classed (name, value) {
13715 var names = classArray(name + "");
13717 if (arguments.length < 2) {
13718 var list = classList(this.node()),
13723 if (!list.contains(names[i])) return false;
13729 return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value));
13732 function textRemove() {
13733 this.textContent = "";
13736 function textConstant(value) {
13737 return function () {
13738 this.textContent = value;
13742 function textFunction(value) {
13743 return function () {
13744 var v = value.apply(this, arguments);
13745 this.textContent = v == null ? "" : v;
13749 function selection_text (value) {
13750 return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction : textConstant)(value)) : this.node().textContent;
13753 function htmlRemove() {
13754 this.innerHTML = "";
13757 function htmlConstant(value) {
13758 return function () {
13759 this.innerHTML = value;
13763 function htmlFunction(value) {
13764 return function () {
13765 var v = value.apply(this, arguments);
13766 this.innerHTML = v == null ? "" : v;
13770 function selection_html (value) {
13771 return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML;
13775 if (this.nextSibling) this.parentNode.appendChild(this);
13778 function selection_raise () {
13779 return this.each(raise);
13783 if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
13786 function selection_lower () {
13787 return this.each(lower);
13790 function selection_append (name) {
13791 var create = typeof name === "function" ? name : creator(name);
13792 return this.select(function () {
13793 return this.appendChild(create.apply(this, arguments));
13797 function constantNull() {
13801 function selection_insert (name, before) {
13802 var create = typeof name === "function" ? name : creator(name),
13803 select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
13804 return this.select(function () {
13805 return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
13809 function remove() {
13810 var parent = this.parentNode;
13811 if (parent) parent.removeChild(this);
13814 function selection_remove () {
13815 return this.each(remove);
13818 function selection_cloneShallow() {
13819 var clone = this.cloneNode(false),
13820 parent = this.parentNode;
13821 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
13824 function selection_cloneDeep() {
13825 var clone = this.cloneNode(true),
13826 parent = this.parentNode;
13827 return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
13830 function selection_clone (deep) {
13831 return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
13834 function selection_datum (value) {
13835 return arguments.length ? this.property("__data__", value) : this.node().__data__;
13838 function contextListener(listener) {
13839 return function (event) {
13840 listener.call(this, event, this.__data__);
13844 function parseTypenames$1(typenames) {
13845 return typenames.trim().split(/^|\s+/).map(function (t) {
13847 i = t.indexOf(".");
13848 if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
13856 function onRemove(typename) {
13857 return function () {
13858 var on = this.__on;
13861 for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
13862 if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
13863 this.removeEventListener(o.type, o.listener, o.options);
13869 if (++i) on.length = i;else delete this.__on;
13873 function onAdd(typename, value, options) {
13874 return function () {
13875 var on = this.__on,
13877 listener = contextListener(value);
13878 if (on) for (var j = 0, m = on.length; j < m; ++j) {
13879 if ((o = on[j]).type === typename.type && o.name === typename.name) {
13880 this.removeEventListener(o.type, o.listener, o.options);
13881 this.addEventListener(o.type, o.listener = listener, o.options = options);
13886 this.addEventListener(typename.type, listener, options);
13888 type: typename.type,
13889 name: typename.name,
13891 listener: listener,
13894 if (!on) this.__on = [o];else on.push(o);
13898 function selection_on (typename, value, options) {
13899 var typenames = parseTypenames$1(typename + ""),
13901 n = typenames.length,
13904 if (arguments.length < 2) {
13905 var on = this.node().__on;
13907 if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
13908 for (i = 0, o = on[j]; i < n; ++i) {
13909 if ((t = typenames[i]).type === o.type && t.name === o.name) {
13917 on = value ? onAdd : onRemove;
13919 for (i = 0; i < n; ++i) {
13920 this.each(on(typenames[i], value, options));
13926 function dispatchEvent$1(node, type, params) {
13927 var window = defaultView(node),
13928 event = window.CustomEvent;
13930 if (typeof event === "function") {
13931 event = new event(type, params);
13933 event = window.document.createEvent("Event");
13934 if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;else event.initEvent(type, false, false);
13937 node.dispatchEvent(event);
13940 function dispatchConstant(type, params) {
13941 return function () {
13942 return dispatchEvent$1(this, type, params);
13946 function dispatchFunction(type, params) {
13947 return function () {
13948 return dispatchEvent$1(this, type, params.apply(this, arguments));
13952 function selection_dispatch (type, params) {
13953 return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params));
13956 var _marked$2 = /*#__PURE__*/regeneratorRuntime.mark(_callee);
13958 function _callee() {
13959 var groups, j, m, group, i, n, node;
13960 return regeneratorRuntime.wrap(function _callee$(_context) {
13962 switch (_context.prev = _context.next) {
13964 groups = this._groups, j = 0, m = groups.length;
13968 _context.next = 13;
13972 group = groups[j], i = 0, n = group.length;
13976 _context.next = 10;
13980 if (!(node = group[i])) {
14000 return _context.stop();
14003 }, _marked$2, this);
14007 function Selection(groups, parents) {
14008 this._groups = groups;
14009 this._parents = parents;
14012 function selection() {
14013 return new Selection([[document.documentElement]], root);
14016 function selection_selection() {
14020 Selection.prototype = selection.prototype = _defineProperty({
14021 constructor: Selection,
14022 select: selection_select,
14023 selectAll: selection_selectAll,
14024 selectChild: selection_selectChild,
14025 selectChildren: selection_selectChildren,
14026 filter: selection_filter,
14027 data: selection_data,
14028 enter: selection_enter,
14029 exit: selection_exit,
14030 join: selection_join,
14031 merge: selection_merge,
14032 selection: selection_selection,
14033 order: selection_order,
14034 sort: selection_sort,
14035 call: selection_call,
14036 nodes: selection_nodes,
14037 node: selection_node,
14038 size: selection_size,
14039 empty: selection_empty,
14040 each: selection_each,
14041 attr: selection_attr,
14042 style: selection_style,
14043 property: selection_property,
14044 classed: selection_classed,
14045 text: selection_text,
14046 html: selection_html,
14047 raise: selection_raise,
14048 lower: selection_lower,
14049 append: selection_append,
14050 insert: selection_insert,
14051 remove: selection_remove,
14052 clone: selection_clone,
14053 datum: selection_datum,
14055 dispatch: selection_dispatch
14056 }, Symbol.iterator, _callee);
14058 function select (selector) {
14059 return typeof selector === "string" ? new Selection([[document.querySelector(selector)]], [document.documentElement]) : new Selection([[selector]], root);
14062 function sourceEvent (event) {
14065 while (sourceEvent = event.sourceEvent) {
14066 event = sourceEvent;
14072 function pointer (event, node) {
14073 event = sourceEvent(event);
14074 if (node === undefined) node = event.currentTarget;
14077 var svg = node.ownerSVGElement || node;
14079 if (svg.createSVGPoint) {
14080 var point = svg.createSVGPoint();
14081 point.x = event.clientX, point.y = event.clientY;
14082 point = point.matrixTransform(node.getScreenCTM().inverse());
14083 return [point.x, point.y];
14086 if (node.getBoundingClientRect) {
14087 var rect = node.getBoundingClientRect();
14088 return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
14092 return [event.pageX, event.pageY];
14095 function selectAll (selector) {
14096 return typeof selector === "string" ? new Selection([document.querySelectorAll(selector)], [document.documentElement]) : new Selection([selector == null ? [] : array(selector)], root);
14099 function nopropagation(event) {
14100 event.stopImmediatePropagation();
14102 function noevent (event) {
14103 event.preventDefault();
14104 event.stopImmediatePropagation();
14107 function dragDisable (view) {
14108 var root = view.document.documentElement,
14109 selection = select(view).on("dragstart.drag", noevent, true);
14111 if ("onselectstart" in root) {
14112 selection.on("selectstart.drag", noevent, true);
14114 root.__noselect = root.style.MozUserSelect;
14115 root.style.MozUserSelect = "none";
14118 function yesdrag(view, noclick) {
14119 var root = view.document.documentElement,
14120 selection = select(view).on("dragstart.drag", null);
14123 selection.on("click.drag", noevent, true);
14124 setTimeout(function () {
14125 selection.on("click.drag", null);
14129 if ("onselectstart" in root) {
14130 selection.on("selectstart.drag", null);
14132 root.style.MozUserSelect = root.__noselect;
14133 delete root.__noselect;
14137 var constant$1 = (function (x) {
14138 return function () {
14143 // `Object.defineProperties` method
14144 // https://tc39.github.io/ecma262/#sec-object.defineproperties
14145 _export({ target: 'Object', stat: true, forced: !descriptors, sham: !descriptors }, {
14146 defineProperties: objectDefineProperties
14149 function DragEvent(type, _ref) {
14150 var sourceEvent = _ref.sourceEvent,
14151 subject = _ref.subject,
14152 target = _ref.target,
14153 identifier = _ref.identifier,
14154 active = _ref.active,
14159 dispatch = _ref.dispatch;
14160 Object.defineProperties(this, {
14167 value: sourceEvent,
14217 DragEvent.prototype.on = function () {
14218 var value = this._.on.apply(this._, arguments);
14220 return value === this._ ? this : value;
14223 function defaultFilter(event) {
14224 return !event.ctrlKey && !event.button;
14227 function defaultContainer() {
14228 return this.parentNode;
14231 function defaultSubject(event, d) {
14232 return d == null ? {
14238 function defaultTouchable() {
14239 return navigator.maxTouchPoints || "ontouchstart" in this;
14242 function d3_drag () {
14243 var filter = defaultFilter,
14244 container = defaultContainer,
14245 subject = defaultSubject,
14246 touchable = defaultTouchable,
14248 listeners = dispatch("start", "drag", "end"),
14254 clickDistance2 = 0;
14256 function drag(selection) {
14257 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)");
14260 function mousedowned(event, d) {
14261 if (touchending || !filter.call(this, event, d)) return;
14262 var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse");
14263 if (!gesture) return;
14264 select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
14265 dragDisable(event.view);
14266 nopropagation(event);
14267 mousemoving = false;
14268 mousedownx = event.clientX;
14269 mousedowny = event.clientY;
14270 gesture("start", event);
14273 function mousemoved(event) {
14276 if (!mousemoving) {
14277 var dx = event.clientX - mousedownx,
14278 dy = event.clientY - mousedowny;
14279 mousemoving = dx * dx + dy * dy > clickDistance2;
14282 gestures.mouse("drag", event);
14285 function mouseupped(event) {
14286 select(event.view).on("mousemove.drag mouseup.drag", null);
14287 yesdrag(event.view, mousemoving);
14289 gestures.mouse("end", event);
14292 function touchstarted(event, d) {
14293 if (!filter.call(this, event, d)) return;
14294 var touches = event.changedTouches,
14295 c = container.call(this, event, d),
14296 n = touches.length,
14300 for (i = 0; i < n; ++i) {
14301 if (gesture = beforestart(this, c, event, d, touches[i].identifier, touches[i])) {
14302 nopropagation(event);
14303 gesture("start", event, touches[i]);
14308 function touchmoved(event) {
14309 var touches = event.changedTouches,
14310 n = touches.length,
14314 for (i = 0; i < n; ++i) {
14315 if (gesture = gestures[touches[i].identifier]) {
14317 gesture("drag", event, touches[i]);
14322 function touchended(event) {
14323 var touches = event.changedTouches,
14324 n = touches.length,
14327 if (touchending) clearTimeout(touchending);
14328 touchending = setTimeout(function () {
14329 touchending = null;
14330 }, 500); // Ghost clicks are delayed!
14332 for (i = 0; i < n; ++i) {
14333 if (gesture = gestures[touches[i].identifier]) {
14334 nopropagation(event);
14335 gesture("end", event, touches[i]);
14340 function beforestart(that, container, event, d, identifier, touch) {
14341 var dispatch = listeners.copy(),
14342 p = pointer(touch || event, container),
14346 if ((s = subject.call(that, new DragEvent("beforestart", {
14347 sourceEvent: event,
14349 identifier: identifier,
14356 }), d)) == null) return;
14357 dx = s.x - p[0] || 0;
14358 dy = s.y - p[1] || 0;
14359 return function gesture(type, event, touch) {
14365 gestures[identifier] = gesture, n = active++;
14369 delete gestures[identifier], --active;
14373 p = pointer(touch || event, container), n = active;
14377 dispatch.call(type, that, new DragEvent(type, {
14378 sourceEvent: event,
14381 identifier: identifier,
14392 drag.filter = function (_) {
14393 return arguments.length ? (filter = typeof _ === "function" ? _ : constant$1(!!_), drag) : filter;
14396 drag.container = function (_) {
14397 return arguments.length ? (container = typeof _ === "function" ? _ : constant$1(_), drag) : container;
14400 drag.subject = function (_) {
14401 return arguments.length ? (subject = typeof _ === "function" ? _ : constant$1(_), drag) : subject;
14404 drag.touchable = function (_) {
14405 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$1(!!_), drag) : touchable;
14408 drag.on = function () {
14409 var value = listeners.on.apply(listeners, arguments);
14410 return value === listeners ? drag : value;
14413 drag.clickDistance = function (_) {
14414 return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
14420 var defineProperty$9 = objectDefineProperty.f;
14421 var getOwnPropertyNames$1 = objectGetOwnPropertyNames.f;
14427 var setInternalState$8 = internalState.set;
14431 var MATCH$1 = wellKnownSymbol('match');
14432 var NativeRegExp = global_1.RegExp;
14433 var RegExpPrototype$1 = NativeRegExp.prototype;
14437 // "new" should create a new object, old webkit bug
14438 var CORRECT_NEW = new NativeRegExp(re1) !== re1;
14440 var UNSUPPORTED_Y$2 = regexpStickyHelpers.UNSUPPORTED_Y;
14442 var FORCED$b = descriptors && isForced_1('RegExp', (!CORRECT_NEW || UNSUPPORTED_Y$2 || fails(function () {
14443 re2[MATCH$1] = false;
14444 // RegExp constructor can alter flags and IsRegExp works correct with @@match
14445 return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';
14448 // `RegExp` constructor
14449 // https://tc39.github.io/ecma262/#sec-regexp-constructor
14451 var RegExpWrapper = function RegExp(pattern, flags) {
14452 var thisIsRegExp = this instanceof RegExpWrapper;
14453 var patternIsRegExp = isRegexp(pattern);
14454 var flagsAreUndefined = flags === undefined;
14457 if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) {
14462 if (patternIsRegExp && !flagsAreUndefined) pattern = pattern.source;
14463 } else if (pattern instanceof RegExpWrapper) {
14464 if (flagsAreUndefined) flags = regexpFlags.call(pattern);
14465 pattern = pattern.source;
14468 if (UNSUPPORTED_Y$2) {
14469 sticky = !!flags && flags.indexOf('y') > -1;
14470 if (sticky) flags = flags.replace(/y/g, '');
14473 var result = inheritIfRequired(
14474 CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags),
14475 thisIsRegExp ? this : RegExpPrototype$1,
14479 if (UNSUPPORTED_Y$2 && sticky) setInternalState$8(result, { sticky: sticky });
14483 var proxy = function (key) {
14484 key in RegExpWrapper || defineProperty$9(RegExpWrapper, key, {
14485 configurable: true,
14486 get: function () { return NativeRegExp[key]; },
14487 set: function (it) { NativeRegExp[key] = it; }
14490 var keys$2 = getOwnPropertyNames$1(NativeRegExp);
14492 while (keys$2.length > index) proxy(keys$2[index++]);
14493 RegExpPrototype$1.constructor = RegExpWrapper;
14494 RegExpWrapper.prototype = RegExpPrototype$1;
14495 redefine(global_1, 'RegExp', RegExpWrapper);
14498 // https://tc39.github.io/ecma262/#sec-get-regexp-@@species
14499 setSpecies('RegExp');
14501 function define (constructor, factory, prototype) {
14502 constructor.prototype = factory.prototype = prototype;
14503 prototype.constructor = constructor;
14505 function extend(parent, definition) {
14506 var prototype = Object.create(parent.prototype);
14508 for (var key in definition) {
14509 prototype[key] = definition[key];
14515 function Color() {}
14518 var _brighter = 1 / _darker;
14519 var reI = "\\s*([+-]?\\d+)\\s*",
14520 reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
14521 reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
14522 reHex = /^#([0-9a-f]{3,8})$/,
14523 reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
14524 reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
14525 reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
14526 reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
14527 reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
14528 reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
14530 aliceblue: 0xf0f8ff,
14531 antiquewhite: 0xfaebd7,
14533 aquamarine: 0x7fffd4,
14538 blanchedalmond: 0xffebcd,
14540 blueviolet: 0x8a2be2,
14542 burlywood: 0xdeb887,
14543 cadetblue: 0x5f9ea0,
14544 chartreuse: 0x7fff00,
14545 chocolate: 0xd2691e,
14547 cornflowerblue: 0x6495ed,
14548 cornsilk: 0xfff8dc,
14551 darkblue: 0x00008b,
14552 darkcyan: 0x008b8b,
14553 darkgoldenrod: 0xb8860b,
14554 darkgray: 0xa9a9a9,
14555 darkgreen: 0x006400,
14556 darkgrey: 0xa9a9a9,
14557 darkkhaki: 0xbdb76b,
14558 darkmagenta: 0x8b008b,
14559 darkolivegreen: 0x556b2f,
14560 darkorange: 0xff8c00,
14561 darkorchid: 0x9932cc,
14563 darksalmon: 0xe9967a,
14564 darkseagreen: 0x8fbc8f,
14565 darkslateblue: 0x483d8b,
14566 darkslategray: 0x2f4f4f,
14567 darkslategrey: 0x2f4f4f,
14568 darkturquoise: 0x00ced1,
14569 darkviolet: 0x9400d3,
14570 deeppink: 0xff1493,
14571 deepskyblue: 0x00bfff,
14574 dodgerblue: 0x1e90ff,
14575 firebrick: 0xb22222,
14576 floralwhite: 0xfffaf0,
14577 forestgreen: 0x228b22,
14579 gainsboro: 0xdcdcdc,
14580 ghostwhite: 0xf8f8ff,
14582 goldenrod: 0xdaa520,
14585 greenyellow: 0xadff2f,
14587 honeydew: 0xf0fff0,
14589 indianred: 0xcd5c5c,
14593 lavender: 0xe6e6fa,
14594 lavenderblush: 0xfff0f5,
14595 lawngreen: 0x7cfc00,
14596 lemonchiffon: 0xfffacd,
14597 lightblue: 0xadd8e6,
14598 lightcoral: 0xf08080,
14599 lightcyan: 0xe0ffff,
14600 lightgoldenrodyellow: 0xfafad2,
14601 lightgray: 0xd3d3d3,
14602 lightgreen: 0x90ee90,
14603 lightgrey: 0xd3d3d3,
14604 lightpink: 0xffb6c1,
14605 lightsalmon: 0xffa07a,
14606 lightseagreen: 0x20b2aa,
14607 lightskyblue: 0x87cefa,
14608 lightslategray: 0x778899,
14609 lightslategrey: 0x778899,
14610 lightsteelblue: 0xb0c4de,
14611 lightyellow: 0xffffe0,
14613 limegreen: 0x32cd32,
14617 mediumaquamarine: 0x66cdaa,
14618 mediumblue: 0x0000cd,
14619 mediumorchid: 0xba55d3,
14620 mediumpurple: 0x9370db,
14621 mediumseagreen: 0x3cb371,
14622 mediumslateblue: 0x7b68ee,
14623 mediumspringgreen: 0x00fa9a,
14624 mediumturquoise: 0x48d1cc,
14625 mediumvioletred: 0xc71585,
14626 midnightblue: 0x191970,
14627 mintcream: 0xf5fffa,
14628 mistyrose: 0xffe4e1,
14629 moccasin: 0xffe4b5,
14630 navajowhite: 0xffdead,
14634 olivedrab: 0x6b8e23,
14636 orangered: 0xff4500,
14638 palegoldenrod: 0xeee8aa,
14639 palegreen: 0x98fb98,
14640 paleturquoise: 0xafeeee,
14641 palevioletred: 0xdb7093,
14642 papayawhip: 0xffefd5,
14643 peachpuff: 0xffdab9,
14647 powderblue: 0xb0e0e6,
14649 rebeccapurple: 0x663399,
14651 rosybrown: 0xbc8f8f,
14652 royalblue: 0x4169e1,
14653 saddlebrown: 0x8b4513,
14655 sandybrown: 0xf4a460,
14656 seagreen: 0x2e8b57,
14657 seashell: 0xfff5ee,
14661 slateblue: 0x6a5acd,
14662 slategray: 0x708090,
14663 slategrey: 0x708090,
14665 springgreen: 0x00ff7f,
14666 steelblue: 0x4682b4,
14671 turquoise: 0x40e0d0,
14675 whitesmoke: 0xf5f5f5,
14677 yellowgreen: 0x9acd32
14679 define(Color, color, {
14680 copy: function copy(channels) {
14681 return Object.assign(new this.constructor(), this, channels);
14683 displayable: function displayable() {
14684 return this.rgb().displayable();
14686 hex: color_formatHex,
14687 // Deprecated! Use color.formatHex.
14688 formatHex: color_formatHex,
14689 formatHsl: color_formatHsl,
14690 formatRgb: color_formatRgb,
14691 toString: color_formatRgb
14694 function color_formatHex() {
14695 return this.rgb().formatHex();
14698 function color_formatHsl() {
14699 return hslConvert(this).formatHsl();
14702 function color_formatRgb() {
14703 return this.rgb().formatRgb();
14706 function color(format) {
14708 format = (format + "").trim().toLowerCase();
14709 return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
14710 : l === 3 ? new Rgb(m >> 8 & 0xf | m >> 4 & 0xf0, m >> 4 & 0xf | m & 0xf0, (m & 0xf) << 4 | m & 0xf, 1) // #f00
14711 : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
14712 : 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
14713 : null // invalid hex
14714 ) : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
14715 : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
14716 : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
14717 : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
14718 : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
14719 : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
14720 : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
14721 : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null;
14725 return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
14728 function rgba(r, g, b, a) {
14729 if (a <= 0) r = g = b = NaN;
14730 return new Rgb(r, g, b, a);
14733 function rgbConvert(o) {
14734 if (!(o instanceof Color)) o = color(o);
14735 if (!o) return new Rgb();
14737 return new Rgb(o.r, o.g, o.b, o.opacity);
14739 function rgb(r, g, b, opacity) {
14740 return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
14742 function Rgb(r, g, b, opacity) {
14746 this.opacity = +opacity;
14748 define(Rgb, rgb, extend(Color, {
14749 brighter: function brighter(k) {
14750 k = k == null ? _brighter : Math.pow(_brighter, k);
14751 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
14753 darker: function darker(k) {
14754 k = k == null ? _darker : Math.pow(_darker, k);
14755 return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
14757 rgb: function rgb() {
14760 displayable: function displayable() {
14761 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;
14763 hex: rgb_formatHex,
14764 // Deprecated! Use color.formatHex.
14765 formatHex: rgb_formatHex,
14766 formatRgb: rgb_formatRgb,
14767 toString: rgb_formatRgb
14770 function rgb_formatHex() {
14771 return "#" + hex$2(this.r) + hex$2(this.g) + hex$2(this.b);
14774 function rgb_formatRgb() {
14775 var a = this.opacity;
14776 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
14777 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 + ")");
14780 function hex$2(value) {
14781 value = Math.max(0, Math.min(255, Math.round(value) || 0));
14782 return (value < 16 ? "0" : "") + value.toString(16);
14785 function hsla(h, s, l, a) {
14786 if (a <= 0) h = s = l = NaN;else if (l <= 0 || l >= 1) h = s = NaN;else if (s <= 0) h = NaN;
14787 return new Hsl(h, s, l, a);
14790 function hslConvert(o) {
14791 if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
14792 if (!(o instanceof Color)) o = color(o);
14793 if (!o) return new Hsl();
14794 if (o instanceof Hsl) return o;
14799 min = Math.min(r, g, b),
14800 max = Math.max(r, g, b),
14803 l = (max + min) / 2;
14806 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;
14807 s /= l < 0.5 ? max + min : 2 - max - min;
14810 s = l > 0 && l < 1 ? 0 : h;
14813 return new Hsl(h, s, l, o.opacity);
14815 function hsl(h, s, l, opacity) {
14816 return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
14819 function Hsl(h, s, l, opacity) {
14823 this.opacity = +opacity;
14826 define(Hsl, hsl, extend(Color, {
14827 brighter: function brighter(k) {
14828 k = k == null ? _brighter : Math.pow(_brighter, k);
14829 return new Hsl(this.h, this.s, this.l * k, this.opacity);
14831 darker: function darker(k) {
14832 k = k == null ? _darker : Math.pow(_darker, k);
14833 return new Hsl(this.h, this.s, this.l * k, this.opacity);
14835 rgb: function rgb() {
14836 var h = this.h % 360 + (this.h < 0) * 360,
14837 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
14839 m2 = l + (l < 0.5 ? l : 1 - l) * s,
14841 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);
14843 displayable: function displayable() {
14844 return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1;
14846 formatHsl: function formatHsl() {
14847 var a = this.opacity;
14848 a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
14849 return (a === 1 ? "hsl(" : "hsla(") + (this.h || 0) + ", " + (this.s || 0) * 100 + "%, " + (this.l || 0) * 100 + "%" + (a === 1 ? ")" : ", " + a + ")");
14852 /* From FvD 13.37, CSS Color Module Level 3 */
14854 function hsl2rgb(h, m1, m2) {
14855 return (h < 60 ? m1 + (m2 - m1) * h / 60 : h < 180 ? m2 : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60 : m1) * 255;
14858 var constant$2 = (function (x) {
14859 return function () {
14864 function linear(a, d) {
14865 return function (t) {
14870 function exponential(a, b, y) {
14871 return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function (t) {
14872 return Math.pow(a + t * b, y);
14875 function gamma(y) {
14876 return (y = +y) === 1 ? nogamma : function (a, b) {
14877 return b - a ? exponential(a, b, y) : constant$2(isNaN(a) ? b : a);
14880 function nogamma(a, b) {
14882 return d ? linear(a, d) : constant$2(isNaN(a) ? b : a);
14885 var d3_interpolateRgb = (function rgbGamma(y) {
14886 var color = gamma(y);
14888 function rgb$1(start, end) {
14889 var r = color((start = rgb(start)).r, (end = rgb(end)).r),
14890 g = color(start.g, end.g),
14891 b = color(start.b, end.b),
14892 opacity = nogamma(start.opacity, end.opacity);
14893 return function (t) {
14897 start.opacity = opacity(t);
14902 rgb$1.gamma = rgbGamma;
14906 function numberArray (a, b) {
14908 var n = a ? Math.min(b.length, a.length) : 0,
14911 return function (t) {
14912 for (i = 0; i < n; ++i) {
14913 c[i] = a[i] * (1 - t) + b[i] * t;
14919 function isNumberArray(x) {
14920 return ArrayBuffer.isView(x) && !(x instanceof DataView);
14923 function genericArray(a, b) {
14924 var nb = b ? b.length : 0,
14925 na = a ? Math.min(nb, a.length) : 0,
14930 for (i = 0; i < na; ++i) {
14931 x[i] = interpolate(a[i], b[i]);
14934 for (; i < nb; ++i) {
14938 return function (t) {
14939 for (i = 0; i < na; ++i) {
14947 function date (a, b) {
14948 var d = new Date();
14949 return a = +a, b = +b, function (t) {
14950 return d.setTime(a * (1 - t) + b * t), d;
14954 function d3_interpolateNumber (a, b) {
14955 return a = +a, b = +b, function (t) {
14956 return a * (1 - t) + b * t;
14960 function object (a, b) {
14964 if (a === null || _typeof(a) !== "object") a = {};
14965 if (b === null || _typeof(b) !== "object") b = {};
14969 i[k] = interpolate(a[k], b[k]);
14975 return function (t) {
14984 var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
14985 reB = new RegExp(reA.source, "g");
14988 return function () {
14994 return function (t) {
14999 function interpolateString (a, b) {
15000 var bi = reA.lastIndex = reB.lastIndex = 0,
15001 // scan index for next number in b
15003 // current match in a
15005 // current match in b
15007 // string preceding current number in b, if any
15011 // string constants and placeholders
15012 q = []; // number interpolators
15013 // Coerce inputs to strings.
15015 a = a + "", b = b + ""; // Interpolate pairs of numbers in a & b.
15017 while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
15018 if ((bs = bm.index) > bi) {
15019 // a string precedes the next number in b
15020 bs = b.slice(bi, bs);
15021 if (s[i]) s[i] += bs; // coalesce with previous string
15025 if ((am = am[0]) === (bm = bm[0])) {
15026 // numbers in a & b match
15027 if (s[i]) s[i] += bm; // coalesce with previous string
15030 // interpolate non-matching numbers
15034 x: d3_interpolateNumber(am, bm)
15038 bi = reB.lastIndex;
15039 } // Add remains of b.
15042 if (bi < b.length) {
15044 if (s[i]) s[i] += bs; // coalesce with previous string
15046 } // Special optimization for only a single match.
15047 // Otherwise, interpolate each of the numbers and rejoin the string.
15050 return s.length < 2 ? q[0] ? one(q[0].x) : zero(b) : (b = q.length, function (t) {
15051 for (var i = 0, o; i < b; ++i) {
15052 s[(o = q[i]).i] = o.x(t);
15059 function interpolate (a, b) {
15060 var t = _typeof(b),
15063 return b == null || t === "boolean" ? constant$2(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);
15066 function interpolateRound (a, b) {
15067 return a = +a, b = +b, function (t) {
15068 return Math.round(a * (1 - t) + b * t);
15072 var degrees$1 = 180 / Math.PI;
15081 function decompose (a, b, c, d, e, f) {
15082 var scaleX, scaleY, skewX;
15083 if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
15084 if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
15085 if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
15086 if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
15090 rotate: Math.atan2(b, a) * degrees$1,
15091 skewX: Math.atan(skewX) * degrees$1,
15098 /* eslint-disable no-undef */
15100 function parseCss(value) {
15101 var m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + "");
15102 return m.isIdentity ? identity$1 : decompose(m.a, m.b, m.c, m.d, m.e, m.f);
15104 function parseSvg(value) {
15105 if (value == null) return identity$1;
15106 if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
15107 svgNode.setAttribute("transform", value);
15108 if (!(value = svgNode.transform.baseVal.consolidate())) return identity$1;
15109 value = value.matrix;
15110 return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
15113 function interpolateTransform(parse, pxComma, pxParen, degParen) {
15115 return s.length ? s.pop() + " " : "";
15118 function translate(xa, ya, xb, yb, s, q) {
15119 if (xa !== xb || ya !== yb) {
15120 var i = s.push("translate(", null, pxComma, null, pxParen);
15123 x: d3_interpolateNumber(xa, xb)
15126 x: d3_interpolateNumber(ya, yb)
15128 } else if (xb || yb) {
15129 s.push("translate(" + xb + pxComma + yb + pxParen);
15133 function rotate(a, b, s, q) {
15135 if (a - b > 180) b += 360;else if (b - a > 180) a += 360; // shortest path
15138 i: s.push(pop(s) + "rotate(", null, degParen) - 2,
15139 x: d3_interpolateNumber(a, b)
15142 s.push(pop(s) + "rotate(" + b + degParen);
15146 function skewX(a, b, s, q) {
15149 i: s.push(pop(s) + "skewX(", null, degParen) - 2,
15150 x: d3_interpolateNumber(a, b)
15153 s.push(pop(s) + "skewX(" + b + degParen);
15157 function scale(xa, ya, xb, yb, s, q) {
15158 if (xa !== xb || ya !== yb) {
15159 var i = s.push(pop(s) + "scale(", null, ",", null, ")");
15162 x: d3_interpolateNumber(xa, xb)
15165 x: d3_interpolateNumber(ya, yb)
15167 } else if (xb !== 1 || yb !== 1) {
15168 s.push(pop(s) + "scale(" + xb + "," + yb + ")");
15172 return function (a, b) {
15174 // string constants and placeholders
15175 q = []; // number interpolators
15177 a = parse(a), b = parse(b);
15178 translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
15179 rotate(a.rotate, b.rotate, s, q);
15180 skewX(a.skewX, b.skewX, s, q);
15181 scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
15182 a = b = null; // gc
15184 return function (t) {
15190 s[(o = q[i]).i] = o.x(t);
15198 var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
15199 var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
15201 var epsilon2$1 = 1e-12;
15204 return ((x = Math.exp(x)) + 1 / x) / 2;
15208 return ((x = Math.exp(x)) - 1 / x) / 2;
15212 return ((x = Math.exp(2 * x)) - 1) / (x + 1);
15215 var interpolateZoom = (function zoomRho(rho, rho2, rho4) {
15216 // p0 = [ux0, uy0, w0]
15217 // p1 = [ux1, uy1, w1]
15218 function zoom(p0, p1) {
15227 d2 = dx * dx + dy * dy,
15229 S; // Special case for u0 ≅ u1.
15231 if (d2 < epsilon2$1) {
15232 S = Math.log(w1 / w0) / rho;
15234 i = function i(t) {
15235 return [ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(rho * t * S)];
15239 var d1 = Math.sqrt(d2),
15240 b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
15241 b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
15242 r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
15243 r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
15244 S = (r1 - r0) / rho;
15246 i = function i(t) {
15249 u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
15250 return [ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / cosh(rho * s + r0)];
15254 i.duration = S * 1000 * rho / Math.SQRT2;
15258 zoom.rho = function (_) {
15259 var _1 = Math.max(1e-3, +_),
15263 return zoomRho(_1, _2, _4);
15267 })(Math.SQRT2, 2, 4);
15269 function d3_quantize (interpolator, n) {
15270 var samples = new Array(n);
15272 for (var i = 0; i < n; ++i) {
15273 samples[i] = interpolator(i / (n - 1));
15279 // `Function.prototype.bind` method
15280 // https://tc39.github.io/ecma262/#sec-function.prototype.bind
15281 _export({ target: 'Function', proto: true }, {
15286 // is an animation frame pending?
15288 // is a timeout pending?
15290 // are any timers active?
15292 // how frequently we check for clock skew
15298 clock = (typeof performance === "undefined" ? "undefined" : _typeof(performance)) === "object" && performance.now ? performance : Date,
15299 setFrame = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function (f) {
15303 return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
15306 function clearNow() {
15311 this._call = this._time = this._next = null;
15313 Timer.prototype = timer.prototype = {
15314 constructor: Timer,
15315 restart: function restart(callback, delay, time) {
15316 if (typeof callback !== "function") throw new TypeError("callback is not a function");
15317 time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
15319 if (!this._next && taskTail !== this) {
15320 if (taskTail) taskTail._next = this;else taskHead = this;
15324 this._call = callback;
15328 stop: function stop() {
15331 this._time = Infinity;
15336 function timer(callback, delay, time) {
15337 var t = new Timer();
15338 t.restart(callback, delay, time);
15341 function timerFlush() {
15342 now(); // Get the current time, if not already set.
15344 ++frame; // Pretend we’ve set an alarm, if we haven’t already.
15350 if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
15358 clockNow = (clockLast = clock.now()) + clockSkew;
15359 frame = timeout = 0;
15371 var now = clock.now(),
15372 delay = now - clockLast;
15373 if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
15384 if (time > t1._time) time = t1._time;
15385 t0 = t1, t1 = t1._next;
15387 t2 = t1._next, t1._next = null;
15388 t1 = t0 ? t0._next = t2 : taskHead = t2;
15396 function sleep(time) {
15397 if (frame) return; // Soonest alarm already set, or will be.
15399 if (timeout) timeout = clearTimeout(timeout);
15400 var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
15403 if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
15404 if (interval) interval = clearInterval(interval);
15406 if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
15407 frame = 1, setFrame(wake);
15411 function d3_timeout (callback, delay, time) {
15412 var t = new Timer();
15413 delay = delay == null ? 0 : +delay;
15414 t.restart(function (elapsed) {
15416 callback(elapsed + delay);
15421 var emptyOn = dispatch("start", "end", "cancel", "interrupt");
15422 var emptyTween = [];
15430 function schedule (node, name, id, index, group, timing) {
15431 var schedules = node.__transition;
15432 if (!schedules) node.__transition = {};else if (id in schedules) return;
15436 // For context during callback.
15438 // For context during callback.
15442 delay: timing.delay,
15443 duration: timing.duration,
15449 function init(node, id) {
15450 var schedule = get$4(node, id);
15451 if (schedule.state > CREATED) throw new Error("too late; already scheduled");
15454 function set$4(node, id) {
15455 var schedule = get$4(node, id);
15456 if (schedule.state > STARTED) throw new Error("too late; already running");
15459 function get$4(node, id) {
15460 var schedule = node.__transition;
15461 if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
15465 function create(node, id, self) {
15466 var schedules = node.__transition,
15467 tween; // Initialize the self timer when the transition is created.
15468 // Note the actual delay is not known until the first callback!
15470 schedules[id] = self;
15471 self.timer = timer(schedule, 0, self.time);
15473 function schedule(elapsed) {
15474 self.state = SCHEDULED;
15475 self.timer.restart(start, self.delay, self.time); // If the elapsed delay is less than our first sleep, start immediately.
15477 if (self.delay <= elapsed) start(elapsed - self.delay);
15480 function start(elapsed) {
15481 var i, j, n, o; // If the state is not SCHEDULED, then we previously errored on start.
15483 if (self.state !== SCHEDULED) return stop();
15485 for (i in schedules) {
15487 if (o.name !== self.name) continue; // While this element already has a starting transition during this frame,
15488 // defer starting an interrupting transition until that transition has a
15489 // chance to tick (and possibly end); see d3/d3-transition#54!
15491 if (o.state === STARTED) return d3_timeout(start); // Interrupt the active transition, if any.
15493 if (o.state === RUNNING) {
15496 o.on.call("interrupt", node, node.__data__, o.index, o.group);
15497 delete schedules[i];
15498 } // Cancel any pre-empted transitions.
15499 else if (+i < id) {
15502 o.on.call("cancel", node, node.__data__, o.index, o.group);
15503 delete schedules[i];
15505 } // Defer the first tick to end of the current frame; see d3/d3#1576.
15506 // Note the transition may be canceled after start and before the first tick!
15507 // Note this must be scheduled before the start event; see d3/d3-transition#16!
15508 // Assuming this is successful, subsequent callbacks go straight to tick.
15511 d3_timeout(function () {
15512 if (self.state === STARTED) {
15513 self.state = RUNNING;
15514 self.timer.restart(tick, self.delay, self.time);
15517 }); // Dispatch the start event.
15518 // Note this must be done before the tween are initialized.
15520 self.state = STARTING;
15521 self.on.call("start", node, node.__data__, self.index, self.group);
15522 if (self.state !== STARTING) return; // interrupted
15524 self.state = STARTED; // Initialize the tween, deleting null tween.
15526 tween = new Array(n = self.tween.length);
15528 for (i = 0, j = -1; i < n; ++i) {
15529 if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
15534 tween.length = j + 1;
15537 function tick(elapsed) {
15538 var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
15543 tween[i].call(node, t);
15544 } // Dispatch the end event.
15547 if (self.state === ENDING) {
15548 self.on.call("end", node, node.__data__, self.index, self.group);
15554 self.state = ENDED;
15556 delete schedules[id];
15558 for (var i in schedules) {
15560 } // eslint-disable-line no-unused-vars
15563 delete node.__transition;
15567 function interrupt (node, name) {
15568 var schedules = node.__transition,
15573 if (!schedules) return;
15574 name = name == null ? null : name + "";
15576 for (i in schedules) {
15577 if ((schedule = schedules[i]).name !== name) {
15582 active = schedule.state > STARTING && schedule.state < ENDING;
15583 schedule.state = ENDED;
15584 schedule.timer.stop();
15585 schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
15586 delete schedules[i];
15589 if (empty) delete node.__transition;
15592 function selection_interrupt (name) {
15593 return this.each(function () {
15594 interrupt(this, name);
15598 function tweenRemove(id, name) {
15599 var tween0, tween1;
15600 return function () {
15601 var schedule = set$4(this, id),
15602 tween = schedule.tween; // If this node shared tween with the previous node,
15603 // just assign the updated shared tween and we’re done!
15604 // Otherwise, copy-on-write.
15606 if (tween !== tween0) {
15607 tween1 = tween0 = tween;
15609 for (var i = 0, n = tween1.length; i < n; ++i) {
15610 if (tween1[i].name === name) {
15611 tween1 = tween1.slice();
15612 tween1.splice(i, 1);
15618 schedule.tween = tween1;
15622 function tweenFunction(id, name, value) {
15623 var tween0, tween1;
15624 if (typeof value !== "function") throw new Error();
15625 return function () {
15626 var schedule = set$4(this, id),
15627 tween = schedule.tween; // If this node shared tween with the previous node,
15628 // just assign the updated shared tween and we’re done!
15629 // Otherwise, copy-on-write.
15631 if (tween !== tween0) {
15632 tween1 = (tween0 = tween).slice();
15637 }, i = 0, n = tween1.length; i < n; ++i) {
15638 if (tween1[i].name === name) {
15644 if (i === n) tween1.push(t);
15647 schedule.tween = tween1;
15651 function transition_tween (name, value) {
15655 if (arguments.length < 2) {
15656 var tween = get$4(this.node(), id).tween;
15658 for (var i = 0, n = tween.length, t; i < n; ++i) {
15659 if ((t = tween[i]).name === name) {
15667 return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
15669 function tweenValue(transition, name, value) {
15670 var id = transition._id;
15671 transition.each(function () {
15672 var schedule = set$4(this, id);
15673 (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
15675 return function (node) {
15676 return get$4(node, id).value[name];
15680 function interpolate$1 (a, b) {
15682 return (typeof b === "number" ? d3_interpolateNumber : b instanceof color ? d3_interpolateRgb : (c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)(a, b);
15685 function attrRemove$1(name) {
15686 return function () {
15687 this.removeAttribute(name);
15691 function attrRemoveNS$1(fullname) {
15692 return function () {
15693 this.removeAttributeNS(fullname.space, fullname.local);
15697 function attrConstant$1(name, interpolate, value1) {
15699 string1 = value1 + "",
15701 return function () {
15702 var string0 = this.getAttribute(name);
15703 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
15707 function attrConstantNS$1(fullname, interpolate, value1) {
15709 string1 = value1 + "",
15711 return function () {
15712 var string0 = this.getAttributeNS(fullname.space, fullname.local);
15713 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
15717 function attrFunction$1(name, interpolate, value) {
15718 var string00, string10, interpolate0;
15719 return function () {
15721 value1 = value(this),
15723 if (value1 == null) return void this.removeAttribute(name);
15724 string0 = this.getAttribute(name);
15725 string1 = value1 + "";
15726 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
15730 function attrFunctionNS$1(fullname, interpolate, value) {
15731 var string00, string10, interpolate0;
15732 return function () {
15734 value1 = value(this),
15736 if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
15737 string0 = this.getAttributeNS(fullname.space, fullname.local);
15738 string1 = value1 + "";
15739 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
15743 function transition_attr (name, value) {
15744 var fullname = namespace(name),
15745 i = fullname === "transform" ? interpolateTransformSvg : interpolate$1;
15746 return this.attrTween(name, typeof value === "function" ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value)) : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname) : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value));
15749 function attrInterpolate(name, i) {
15750 return function (t) {
15751 this.setAttribute(name, i.call(this, t));
15755 function attrInterpolateNS(fullname, i) {
15756 return function (t) {
15757 this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
15761 function attrTweenNS(fullname, value) {
15765 var i = value.apply(this, arguments);
15766 if (i !== i0) t0 = (i0 = i) && attrInterpolateNS(fullname, i);
15770 tween._value = value;
15774 function attrTween(name, value) {
15778 var i = value.apply(this, arguments);
15779 if (i !== i0) t0 = (i0 = i) && attrInterpolate(name, i);
15783 tween._value = value;
15787 function transition_attrTween (name, value) {
15788 var key = "attr." + name;
15789 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
15790 if (value == null) return this.tween(key, null);
15791 if (typeof value !== "function") throw new Error();
15792 var fullname = namespace(name);
15793 return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
15796 function delayFunction(id, value) {
15797 return function () {
15798 init(this, id).delay = +value.apply(this, arguments);
15802 function delayConstant(id, value) {
15803 return value = +value, function () {
15804 init(this, id).delay = value;
15808 function transition_delay (value) {
15810 return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id, value)) : get$4(this.node(), id).delay;
15813 function durationFunction(id, value) {
15814 return function () {
15815 set$4(this, id).duration = +value.apply(this, arguments);
15819 function durationConstant(id, value) {
15820 return value = +value, function () {
15821 set$4(this, id).duration = value;
15825 function transition_duration (value) {
15827 return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id, value)) : get$4(this.node(), id).duration;
15830 function easeConstant(id, value) {
15831 if (typeof value !== "function") throw new Error();
15832 return function () {
15833 set$4(this, id).ease = value;
15837 function transition_ease (value) {
15839 return arguments.length ? this.each(easeConstant(id, value)) : get$4(this.node(), id).ease;
15842 function easeVarying(id, value) {
15843 return function () {
15844 var v = value.apply(this, arguments);
15845 if (typeof v !== "function") throw new Error();
15846 set$4(this, id).ease = v;
15850 function transition_easeVarying (value) {
15851 if (typeof value !== "function") throw new Error();
15852 return this.each(easeVarying(this._id, value));
15855 function transition_filter (match) {
15856 if (typeof match !== "function") match = matcher(match);
15858 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
15859 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
15860 if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
15861 subgroup.push(node);
15866 return new Transition(subgroups, this._parents, this._name, this._id);
15869 function transition_merge (transition) {
15870 if (transition._id !== this._id) throw new Error();
15872 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) {
15873 for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
15874 if (node = group0[i] || group1[i]) {
15880 for (; j < m0; ++j) {
15881 merges[j] = groups0[j];
15884 return new Transition(merges, this._parents, this._name, this._id);
15887 function start(name) {
15888 return (name + "").trim().split(/^|\s+/).every(function (t) {
15889 var i = t.indexOf(".");
15890 if (i >= 0) t = t.slice(0, i);
15891 return !t || t === "start";
15895 function onFunction(id, name, listener) {
15898 sit = start(name) ? init : set$4;
15899 return function () {
15900 var schedule = sit(this, id),
15901 on = schedule.on; // If this node shared a dispatch with the previous node,
15902 // just assign the updated shared dispatch and we’re done!
15903 // Otherwise, copy-on-write.
15905 if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
15910 function transition_on (name, listener) {
15912 return arguments.length < 2 ? get$4(this.node(), id).on.on(name) : this.each(onFunction(id, name, listener));
15915 function removeFunction(id) {
15916 return function () {
15917 var parent = this.parentNode;
15919 for (var i in this.__transition) {
15920 if (+i !== id) return;
15923 if (parent) parent.removeChild(this);
15927 function transition_remove () {
15928 return this.on("end.remove", removeFunction(this._id));
15931 function transition_select (select) {
15932 var name = this._name,
15934 if (typeof select !== "function") select = selector(select);
15936 for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
15937 for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
15938 if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
15939 if ("__data__" in node) subnode.__data__ = node.__data__;
15940 subgroup[i] = subnode;
15941 schedule(subgroup[i], name, id, i, subgroup, get$4(node, id));
15946 return new Transition(subgroups, this._parents, name, id);
15949 function transition_selectAll (select) {
15950 var name = this._name,
15952 if (typeof select !== "function") select = selectorAll(select);
15954 for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
15955 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
15956 if (node = group[i]) {
15957 for (var children = select.call(node, node.__data__, i, group), child, inherit = get$4(node, id), k = 0, l = children.length; k < l; ++k) {
15958 if (child = children[k]) {
15959 schedule(child, name, id, k, children, inherit);
15963 subgroups.push(children);
15964 parents.push(node);
15969 return new Transition(subgroups, parents, name, id);
15972 var Selection$1 = selection.prototype.constructor;
15973 function transition_selection () {
15974 return new Selection$1(this._groups, this._parents);
15977 function styleNull(name, interpolate) {
15978 var string00, string10, interpolate0;
15979 return function () {
15980 var string0 = styleValue(this, name),
15981 string1 = (this.style.removeProperty(name), styleValue(this, name));
15982 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate(string00 = string0, string10 = string1);
15986 function styleRemove$1(name) {
15987 return function () {
15988 this.style.removeProperty(name);
15992 function styleConstant$1(name, interpolate, value1) {
15994 string1 = value1 + "",
15996 return function () {
15997 var string0 = styleValue(this, name);
15998 return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate(string00 = string0, value1);
16002 function styleFunction$1(name, interpolate, value) {
16003 var string00, string10, interpolate0;
16004 return function () {
16005 var string0 = styleValue(this, name),
16006 value1 = value(this),
16007 string1 = value1 + "";
16008 if (value1 == null) string1 = value1 = (this.style.removeProperty(name), styleValue(this, name));
16009 return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
16013 function styleMaybeRemove(id, name) {
16017 key = "style." + name,
16018 event = "end." + key,
16020 return function () {
16021 var schedule = set$4(this, id),
16023 listener = schedule.value[key] == null ? remove || (remove = styleRemove$1(name)) : undefined; // If this node shared a dispatch with the previous node,
16024 // just assign the updated shared dispatch and we’re done!
16025 // Otherwise, copy-on-write.
16027 if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
16032 function transition_style (name, value, priority) {
16033 var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1;
16034 return value == null ? this.styleTween(name, styleNull(name, i)).on("end.style." + name, styleRemove$1(name)) : typeof value === "function" ? this.styleTween(name, styleFunction$1(name, i, tweenValue(this, "style." + name, value))).each(styleMaybeRemove(this._id, name)) : this.styleTween(name, styleConstant$1(name, i, value), priority).on("end.style." + name, null);
16037 function styleInterpolate(name, i, priority) {
16038 return function (t) {
16039 this.style.setProperty(name, i.call(this, t), priority);
16043 function styleTween(name, value, priority) {
16047 var i = value.apply(this, arguments);
16048 if (i !== i0) t = (i0 = i) && styleInterpolate(name, i, priority);
16052 tween._value = value;
16056 function transition_styleTween (name, value, priority) {
16057 var key = "style." + (name += "");
16058 if (arguments.length < 2) return (key = this.tween(key)) && key._value;
16059 if (value == null) return this.tween(key, null);
16060 if (typeof value !== "function") throw new Error();
16061 return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
16064 function textConstant$1(value) {
16065 return function () {
16066 this.textContent = value;
16070 function textFunction$1(value) {
16071 return function () {
16072 var value1 = value(this);
16073 this.textContent = value1 == null ? "" : value1;
16077 function transition_text (value) {
16078 return this.tween("text", typeof value === "function" ? textFunction$1(tweenValue(this, "text", value)) : textConstant$1(value == null ? "" : value + ""));
16081 function textInterpolate(i) {
16082 return function (t) {
16083 this.textContent = i.call(this, t);
16087 function textTween(value) {
16091 var i = value.apply(this, arguments);
16092 if (i !== i0) t0 = (i0 = i) && textInterpolate(i);
16096 tween._value = value;
16100 function transition_textTween (value) {
16102 if (arguments.length < 1) return (key = this.tween(key)) && key._value;
16103 if (value == null) return this.tween(key, null);
16104 if (typeof value !== "function") throw new Error();
16105 return this.tween(key, textTween(value));
16108 function transition_transition () {
16109 var name = this._name,
16113 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16114 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16115 if (node = group[i]) {
16116 var inherit = get$4(node, id0);
16117 schedule(node, name, id1, i, group, {
16118 time: inherit.time + inherit.delay + inherit.duration,
16120 duration: inherit.duration,
16127 return new Transition(groups, this._parents, name, id1);
16130 function transition_end () {
16135 size = that.size();
16136 return new Promise(function (resolve, reject) {
16141 value: function value() {
16142 if (--size === 0) resolve();
16145 that.each(function () {
16146 var schedule = set$4(this, id),
16147 on = schedule.on; // If this node shared a dispatch with the previous node,
16148 // just assign the updated shared dispatch and we’re done!
16149 // Otherwise, copy-on-write.
16152 on1 = (on0 = on).copy();
16154 on1._.cancel.push(cancel);
16156 on1._.interrupt.push(cancel);
16158 on1._.end.push(end);
16162 }); // The selection was empty, resolve end immediately
16164 if (size === 0) resolve();
16169 function Transition(groups, parents, name, id) {
16170 this._groups = groups;
16171 this._parents = parents;
16175 function transition(name) {
16176 return selection().transition(name);
16181 var selection_prototype = selection.prototype;
16182 Transition.prototype = transition.prototype = _defineProperty({
16183 constructor: Transition,
16184 select: transition_select,
16185 selectAll: transition_selectAll,
16186 filter: transition_filter,
16187 merge: transition_merge,
16188 selection: transition_selection,
16189 transition: transition_transition,
16190 call: selection_prototype.call,
16191 nodes: selection_prototype.nodes,
16192 node: selection_prototype.node,
16193 size: selection_prototype.size,
16194 empty: selection_prototype.empty,
16195 each: selection_prototype.each,
16197 attr: transition_attr,
16198 attrTween: transition_attrTween,
16199 style: transition_style,
16200 styleTween: transition_styleTween,
16201 text: transition_text,
16202 textTween: transition_textTween,
16203 remove: transition_remove,
16204 tween: transition_tween,
16205 delay: transition_delay,
16206 duration: transition_duration,
16207 ease: transition_ease,
16208 easeVarying: transition_easeVarying,
16209 end: transition_end
16210 }, Symbol.iterator, selection_prototype[Symbol.iterator]);
16212 var linear$1 = function linear(t) {
16216 function cubicInOut(t) {
16217 return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
16220 var defaultTiming = {
16228 function inherit(node, id) {
16231 while (!(timing = node.__transition) || !(timing = timing[id])) {
16232 if (!(node = node.parentNode)) {
16233 throw new Error("transition ".concat(id, " not found"));
16240 function selection_transition (name) {
16243 if (name instanceof Transition) {
16244 id = name._id, name = name._name;
16246 id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
16249 for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
16250 for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
16251 if (node = group[i]) {
16252 schedule(node, name, id, i, group, timing || inherit(node, id));
16257 return new Transition(groups, this._parents, name, id);
16260 selection.prototype.interrupt = selection_interrupt;
16261 selection.prototype.transition = selection_transition;
16263 var constant$3 = (function (x) {
16264 return function () {
16269 function ZoomEvent(type, _ref) {
16270 var sourceEvent = _ref.sourceEvent,
16271 target = _ref.target,
16272 transform = _ref.transform,
16273 dispatch = _ref.dispatch;
16274 Object.defineProperties(this, {
16281 value: sourceEvent,
16301 function Transform(k, x, y) {
16306 Transform.prototype = {
16307 constructor: Transform,
16308 scale: function scale(k) {
16309 return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
16311 translate: function translate(x, y) {
16312 return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
16314 apply: function apply(point) {
16315 return [point[0] * this.k + this.x, point[1] * this.k + this.y];
16317 applyX: function applyX(x) {
16318 return x * this.k + this.x;
16320 applyY: function applyY(y) {
16321 return y * this.k + this.y;
16323 invert: function invert(location) {
16324 return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
16326 invertX: function invertX(x) {
16327 return (x - this.x) / this.k;
16329 invertY: function invertY(y) {
16330 return (y - this.y) / this.k;
16332 rescaleX: function rescaleX(x) {
16333 return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
16335 rescaleY: function rescaleY(y) {
16336 return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
16338 toString: function toString() {
16339 return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
16342 var identity$2 = new Transform(1, 0, 0);
16344 function nopropagation$1(event) {
16345 event.stopImmediatePropagation();
16347 function noevent$1 (event) {
16348 event.preventDefault();
16349 event.stopImmediatePropagation();
16352 // except for pinch-to-zoom, which is sent as a wheel+ctrlKey event
16354 function defaultFilter$1(event) {
16355 return (!event.ctrlKey || event.type === 'wheel') && !event.button;
16358 function defaultExtent() {
16361 if (e instanceof SVGElement) {
16362 e = e.ownerSVGElement || e;
16364 if (e.hasAttribute("viewBox")) {
16365 e = e.viewBox.baseVal;
16366 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
16369 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
16372 return [[0, 0], [e.clientWidth, e.clientHeight]];
16375 function defaultTransform() {
16376 return this.__zoom || identity$2;
16379 function defaultWheelDelta(event) {
16380 return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);
16383 function defaultTouchable$1() {
16384 return navigator.maxTouchPoints || "ontouchstart" in this;
16387 function defaultConstrain(transform, extent, translateExtent) {
16388 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
16389 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
16390 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
16391 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
16392 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));
16395 function d3_zoom () {
16396 var filter = defaultFilter$1,
16397 extent = defaultExtent,
16398 constrain = defaultConstrain,
16399 wheelDelta = defaultWheelDelta,
16400 touchable = defaultTouchable$1,
16401 scaleExtent = [0, Infinity],
16402 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
16404 interpolate = interpolateZoom,
16405 listeners = dispatch("start", "zoom", "end"),
16411 clickDistance2 = 0,
16414 function zoom(selection) {
16415 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)");
16418 zoom.transform = function (collection, transform, point, event) {
16419 var selection = collection.selection ? collection.selection() : collection;
16420 selection.property("__zoom", defaultTransform);
16422 if (collection !== selection) {
16423 schedule(collection, transform, point, event);
16425 selection.interrupt().each(function () {
16426 gesture(this, arguments).event(event).start().zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform).end();
16431 zoom.scaleBy = function (selection, k, p, event) {
16432 zoom.scaleTo(selection, function () {
16433 var k0 = this.__zoom.k,
16434 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16439 zoom.scaleTo = function (selection, k, p, event) {
16440 zoom.transform(selection, function () {
16441 var e = extent.apply(this, arguments),
16443 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
16444 p1 = t0.invert(p0),
16445 k1 = typeof k === "function" ? k.apply(this, arguments) : k;
16446 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
16450 zoom.translateBy = function (selection, x, y, event) {
16451 zoom.transform(selection, function () {
16452 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);
16456 zoom.translateTo = function (selection, x, y, p, event) {
16457 zoom.transform(selection, function () {
16458 var e = extent.apply(this, arguments),
16460 p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
16461 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);
16465 function scale(transform, k) {
16466 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
16467 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
16470 function translate(transform, p0, p1) {
16471 var x = p0[0] - p1[0] * transform.k,
16472 y = p0[1] - p1[1] * transform.k;
16473 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
16476 function centroid(extent) {
16477 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
16480 function schedule(transition, transform, point, event) {
16481 transition.on("start.zoom", function () {
16482 gesture(this, arguments).event(event).start();
16483 }).on("interrupt.zoom end.zoom", function () {
16484 gesture(this, arguments).event(event).end();
16485 }).tween("zoom", function () {
16488 g = gesture(that, args).event(event),
16489 e = extent.apply(that, args),
16490 p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
16491 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
16493 b = typeof transform === "function" ? transform.apply(that, args) : transform,
16494 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
16495 return function (t) {
16496 if (t === 1) t = b; // Avoid rounding error on end.
16500 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
16507 function gesture(that, args, clean) {
16508 return !clean && that.__zooming || new Gesture(that, args);
16511 function Gesture(that, args) {
16515 this.sourceEvent = null;
16516 this.extent = extent.apply(that, args);
16520 Gesture.prototype = {
16521 event: function event(_event) {
16522 if (_event) this.sourceEvent = _event;
16525 start: function start() {
16526 if (++this.active === 1) {
16527 this.that.__zooming = this;
16528 this.emit("start");
16533 zoom: function zoom(key, transform) {
16534 if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
16535 if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
16536 if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
16537 this.that.__zoom = transform;
16541 end: function end() {
16542 if (--this.active === 0) {
16543 delete this.that.__zooming;
16549 emit: function emit(type) {
16550 var d = select(this.that).datum();
16551 listeners.call(type, this.that, new ZoomEvent(type, {
16552 sourceEvent: this.sourceEvent,
16555 transform: this.that.__zoom,
16556 dispatch: listeners
16561 function wheeled(event) {
16562 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
16563 args[_key - 1] = arguments[_key];
16566 if (!filter.apply(this, arguments)) return;
16567 var g = gesture(this, args).event(event),
16569 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
16570 p = pointer(event); // If the mouse is in the same location as before, reuse it.
16571 // If there were recent wheel events, reset the wheel idle timeout.
16574 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
16575 g.mouse[1] = t.invert(g.mouse[0] = p);
16578 clearTimeout(g.wheel);
16579 } // If this wheel event won’t trigger a transform change, ignore it.
16580 else if (t.k === k) return; // Otherwise, capture the mouse point and location at the start.
16582 g.mouse = [p, t.invert(p)];
16588 g.wheel = setTimeout(wheelidled, wheelDelay);
16589 g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
16591 function wheelidled() {
16597 function mousedowned(event) {
16598 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
16599 args[_key2 - 1] = arguments[_key2];
16602 if (touchending || !filter.apply(this, arguments)) return;
16603 var g = gesture(this, args, true).event(event),
16604 v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
16605 p = pointer(event, currentTarget),
16606 currentTarget = event.currentTarget,
16607 x0 = event.clientX,
16608 y0 = event.clientY;
16609 dragDisable(event.view);
16610 nopropagation$1(event);
16611 g.mouse = [p, this.__zoom.invert(p)];
16615 function mousemoved(event) {
16619 var dx = event.clientX - x0,
16620 dy = event.clientY - y0;
16621 g.moved = dx * dx + dy * dy > clickDistance2;
16624 g.event(event).zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent));
16627 function mouseupped(event) {
16628 v.on("mousemove.zoom mouseup.zoom", null);
16629 yesdrag(event.view, g.moved);
16631 g.event(event).end();
16635 function dblclicked(event) {
16636 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
16637 args[_key3 - 1] = arguments[_key3];
16640 if (!filter.apply(this, arguments)) return;
16641 var t0 = this.__zoom,
16642 p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this),
16643 p1 = t0.invert(p0),
16644 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
16645 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
16647 if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0, event);else select(this).call(zoom.transform, t1, p0, event);
16650 function touchstarted(event) {
16651 for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
16652 args[_key4 - 1] = arguments[_key4];
16655 if (!filter.apply(this, arguments)) return;
16656 var touches = event.touches,
16657 n = touches.length,
16658 g = gesture(this, args, event.changedTouches.length === n).event(event),
16663 nopropagation$1(event);
16665 for (i = 0; i < n; ++i) {
16666 t = touches[i], p = pointer(t, this);
16667 p = [p, this.__zoom.invert(p), t.identifier];
16668 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;
16671 if (touchstarting) touchstarting = clearTimeout(touchstarting);
16674 if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function () {
16675 touchstarting = null;
16682 function touchmoved(event) {
16683 if (!this.__zooming) return;
16685 for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) {
16686 args[_key5 - 1] = arguments[_key5];
16689 var g = gesture(this, args).event(event),
16690 touches = event.changedTouches,
16691 n = touches.length,
16698 for (i = 0; i < n; ++i) {
16699 t = touches[i], p = pointer(t, this);
16700 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;
16706 var p0 = g.touch0[0],
16710 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
16711 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
16712 t = scale(t, Math.sqrt(dp / dl));
16713 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
16714 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
16715 } else if (g.touch0) p = g.touch0[0], l = g.touch0[1];else return;
16717 g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
16720 function touchended(event) {
16721 for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) {
16722 args[_key6 - 1] = arguments[_key6];
16725 if (!this.__zooming) return;
16726 var g = gesture(this, args).event(event),
16727 touches = event.changedTouches,
16728 n = touches.length,
16731 nopropagation$1(event);
16732 if (touchending) clearTimeout(touchending);
16733 touchending = setTimeout(function () {
16734 touchending = null;
16737 for (i = 0; i < n; ++i) {
16739 if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
16742 if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
16743 if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);else {
16744 g.end(); // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
16746 if (g.taps === 2) {
16747 t = pointer(t, this);
16749 if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) {
16750 var p = select(this).on("dblclick.zoom");
16751 if (p) p.apply(this, arguments);
16757 zoom.wheelDelta = function (_) {
16758 return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$3(+_), zoom) : wheelDelta;
16761 zoom.filter = function (_) {
16762 return arguments.length ? (filter = typeof _ === "function" ? _ : constant$3(!!_), zoom) : filter;
16765 zoom.touchable = function (_) {
16766 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$3(!!_), zoom) : touchable;
16769 zoom.extent = function (_) {
16770 return arguments.length ? (extent = typeof _ === "function" ? _ : constant$3([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
16773 zoom.scaleExtent = function (_) {
16774 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
16777 zoom.translateExtent = function (_) {
16778 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]]];
16781 zoom.constrain = function (_) {
16782 return arguments.length ? (constrain = _, zoom) : constrain;
16785 zoom.duration = function (_) {
16786 return arguments.length ? (duration = +_, zoom) : duration;
16789 zoom.interpolate = function (_) {
16790 return arguments.length ? (interpolate = _, zoom) : interpolate;
16793 zoom.on = function () {
16794 var value = listeners.on.apply(listeners, arguments);
16795 return value === listeners ? zoom : value;
16798 zoom.clickDistance = function (_) {
16799 return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
16802 zoom.tapDistance = function (_) {
16803 return arguments.length ? (tapDistance = +_, zoom) : tapDistance;
16810 Bypasses features of D3's default projection stream pipeline that are unnecessary:
16811 * Antimeridian clipping
16812 * Spherical rotation
16816 function geoRawMercator() {
16817 var project = mercatorRaw;
16818 var k = 512 / Math.PI; // scale
16821 var y = 0; // translate
16823 var clipExtent = [[0, 0], [0, 0]];
16825 function projection(point) {
16826 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
16827 return [point[0] * k + x, y - point[1] * k];
16830 projection.invert = function (point) {
16831 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
16832 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
16835 projection.scale = function (_) {
16836 if (!arguments.length) return k;
16841 projection.translate = function (_) {
16842 if (!arguments.length) return [x, y];
16848 projection.clipExtent = function (_) {
16849 if (!arguments.length) return clipExtent;
16854 projection.transform = function (obj) {
16855 if (!arguments.length) return identity$2.translate(x, y).scale(k);
16862 projection.stream = d3_geoTransform({
16863 point: function point(x, y) {
16864 var vec = projection([x, y]);
16865 this.stream.point(vec[0], vec[1]);
16871 function geoOrthoNormalizedDotProduct(a, b, origin) {
16872 if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
16873 return 1; // coincident points, treat as straight and try to remove
16876 return geoVecNormalizedDot(a, b, origin);
16879 function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) {
16880 var val = Math.abs(dotp);
16882 if (val < epsilon) {
16883 return 0; // already orthogonal
16884 } else if (allowStraightAngles && Math.abs(val - 1) < epsilon) {
16885 return 0; // straight angle, which is okay in this case
16886 } else if (val < lowerThreshold || val > upperThreshold) {
16887 return dotp; // can be adjusted
16889 return null; // ignore vertex
16893 function geoOrthoCalcScore(points, isClosed, epsilon, threshold) {
16895 var first = isClosed ? 0 : 1;
16896 var last = isClosed ? points.length : points.length - 1;
16897 var coords = points.map(function (p) {
16900 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
16901 var upperThreshold = Math.cos(threshold * Math.PI / 180);
16903 for (var i = first; i < last; i++) {
16904 var a = coords[(i - 1 + coords.length) % coords.length];
16905 var origin = coords[i];
16906 var b = coords[(i + 1) % coords.length];
16907 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold);
16908 if (dotp === null) continue; // ignore vertex
16910 score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
16914 } // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner
16916 function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) {
16917 var max = -Infinity;
16918 var first = isClosed ? 0 : 1;
16919 var last = isClosed ? coords.length : coords.length - 1;
16921 for (var i = first; i < last; i++) {
16922 var a = coords[(i - 1 + coords.length) % coords.length];
16923 var origin = coords[i];
16924 var b = coords[(i + 1) % coords.length];
16925 var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin);
16926 var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI;
16927 if (angle > 45) angle = 90 - angle;
16928 if (angle >= lessThan) continue;
16929 if (angle > max) max = angle;
16932 if (max === -Infinity) return null;
16934 } // similar to geoOrthoCalcScore, but returns quickly if there is something to do
16936 function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) {
16938 var first = isClosed ? 0 : 1;
16939 var last = isClosed ? coords.length : coords.length - 1;
16940 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
16941 var upperThreshold = Math.cos(threshold * Math.PI / 180);
16943 for (var i = first; i < last; i++) {
16944 var a = coords[(i - 1 + coords.length) % coords.length];
16945 var origin = coords[i];
16946 var b = coords[(i + 1) % coords.length];
16947 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles);
16948 if (dotp === null) continue; // ignore vertex
16950 if (Math.abs(dotp) > 0) return 1; // something to do
16952 score = 0; // already square
16958 var onFreeze = internalMetadata.onFreeze;
16960 var nativeFreeze = Object.freeze;
16961 var FAILS_ON_PRIMITIVES$4 = fails(function () { nativeFreeze(1); });
16963 // `Object.freeze` method
16964 // https://tc39.github.io/ecma262/#sec-object.freeze
16965 _export({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$4, sham: !freezing }, {
16966 freeze: function freeze(it) {
16967 return nativeFreeze && isObject(it) ? nativeFreeze(onFreeze(it)) : it;
16971 // Returns true if a and b have the same elements at the same indices.
16972 function utilArrayIdentical(a, b) {
16973 // an array is always identical to itself
16974 if (a === b) return true;
16976 if (i !== b.length) return false;
16979 if (a[i] !== b[i]) return false;
16983 } // http://2ality.com/2015/01/es6-set-operations.html
16984 // Difference (a \ b): create a set that contains those elements of set a that are not in set b.
16985 // This operation is also sometimes called minus (-).
16986 // var a = [1,2,3];
16987 // var b = [4,3,2];
16988 // utilArrayDifference(a, b)
16990 // utilArrayDifference(b, a)
16993 function utilArrayDifference(a, b) {
16994 var other = new Set(b);
16995 return Array.from(new Set(a)).filter(function (v) {
16996 return !other.has(v);
16998 } // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b.
16999 // var a = [1,2,3];
17000 // var b = [4,3,2];
17001 // utilArrayIntersection(a, b)
17004 function utilArrayIntersection(a, b) {
17005 var other = new Set(b);
17006 return Array.from(new Set(a)).filter(function (v) {
17007 return other.has(v);
17009 } // Union (a ∪ b): create a set that contains the elements of both set a and set b.
17010 // var a = [1,2,3];
17011 // var b = [4,3,2];
17012 // utilArrayUnion(a, b)
17015 function utilArrayUnion(a, b) {
17016 var result = new Set(a);
17017 b.forEach(function (v) {
17020 return Array.from(result);
17021 } // Returns an Array with all the duplicates removed
17022 // var a = [1,1,2,3,3];
17023 // utilArrayUniq(a)
17026 function utilArrayUniq(a) {
17027 return Array.from(new Set(a));
17028 } // Splits array into chunks of given chunk size
17029 // var a = [1,2,3,4,5,6,7];
17030 // utilArrayChunk(a, 3);
17031 // [[1,2,3],[4,5,6],[7]];
17033 function utilArrayChunk(a, chunkSize) {
17034 if (!chunkSize || chunkSize < 0) return [a.slice()];
17035 var result = new Array(Math.ceil(a.length / chunkSize));
17036 return Array.from(result, function (item, i) {
17037 return a.slice(i * chunkSize, i * chunkSize + chunkSize);
17039 } // Flattens two level array into a single level
17040 // var a = [[1,2,3],[4,5,6],[7]];
17041 // utilArrayFlatten(a);
17042 // [1,2,3,4,5,6,7];
17044 function utilArrayFlatten(a) {
17045 return a.reduce(function (acc, val) {
17046 return acc.concat(val);
17048 } // Groups the items of the Array according to the given key
17049 // `key` can be passed as a property or as a key function
17052 // { type: 'Dog', name: 'Spot' },
17053 // { type: 'Cat', name: 'Tiger' },
17054 // { type: 'Dog', name: 'Rover' },
17055 // { type: 'Cat', name: 'Leo' }
17058 // utilArrayGroupBy(pets, 'type')
17060 // 'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}],
17061 // 'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}]
17064 // utilArrayGroupBy(pets, function(item) { return item.name.length; })
17066 // 3: [{type: 'Cat', name: 'Leo'}],
17067 // 4: [{type: 'Dog', name: 'Spot'}],
17068 // 5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}]
17071 function utilArrayGroupBy(a, key) {
17072 return a.reduce(function (acc, item) {
17073 var group = typeof key === 'function' ? key(item) : item[key];
17074 (acc[group] = acc[group] || []).push(item);
17077 } // Returns an Array with all the duplicates removed
17078 // where uniqueness determined by the given key
17079 // `key` can be passed as a property or as a key function
17082 // { type: 'Dog', name: 'Spot' },
17083 // { type: 'Cat', name: 'Tiger' },
17084 // { type: 'Dog', name: 'Rover' },
17085 // { type: 'Cat', name: 'Leo' }
17088 // utilArrayUniqBy(pets, 'type')
17090 // { type: 'Dog', name: 'Spot' },
17091 // { type: 'Cat', name: 'Tiger' }
17094 // utilArrayUniqBy(pets, function(item) { return item.name.length; })
17096 // { type: 'Dog', name: 'Spot' },
17097 // { type: 'Cat', name: 'Tiger' },
17098 // { type: 'Cat', name: 'Leo' }
17101 function utilArrayUniqBy(a, key) {
17102 var seen = new Set();
17103 return a.reduce(function (acc, item) {
17104 var val = typeof key === 'function' ? key(item) : item[key];
17106 if (val && !seen.has(val)) {
17116 fixRegexpWellKnownSymbolLogic('match', 1, function (MATCH, nativeMatch, maybeCallNative) {
17118 // `String.prototype.match` method
17119 // https://tc39.github.io/ecma262/#sec-string.prototype.match
17120 function match(regexp) {
17121 var O = requireObjectCoercible(this);
17122 var matcher = regexp == undefined ? undefined : regexp[MATCH];
17123 return matcher !== undefined ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
17125 // `RegExp.prototype[@@match]` method
17126 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@match
17127 function (regexp) {
17128 var res = maybeCallNative(nativeMatch, regexp, this);
17129 if (res.done) return res.value;
17131 var rx = anObject(regexp);
17132 var S = String(this);
17134 if (!rx.global) return regexpExecAbstract(rx, S);
17136 var fullUnicode = rx.unicode;
17141 while ((result = regexpExecAbstract(rx, S)) !== null) {
17142 var matchStr = String(result[0]);
17144 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
17147 return n === 0 ? null : A;
17152 var remove$1 = removeDiacritics;
17153 var replacementList = [{
17161 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"
17167 chars: "\xC6\u01FC\u01E2"
17176 chars: "\uA738\uA73A"
17182 chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181"
17185 chars: "\u24B8\uFF23\uA73E\u1E08\u0106C\u0108\u010A\u010C\xC7\u0187\u023B"
17188 chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779"
17194 chars: "\u01F1\u01C4"
17197 chars: "\u01F2\u01C5"
17200 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"
17203 chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B"
17206 chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262"
17209 chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D"
17212 chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197"
17215 chars: "\u24BF\uFF2A\u0134\u0248\u0237"
17218 chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2"
17221 chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780"
17230 chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB"
17233 chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E"
17242 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"
17257 chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754"
17260 chars: "\u24C6\uFF31\uA756\uA758\u024A"
17263 chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782"
17266 chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784"
17269 chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786"
17278 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"
17281 chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245"
17287 chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72"
17290 chars: "\u24CD\uFF38\u1E8A\u1E8C"
17293 chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE"
17296 chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762"
17299 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"
17305 chars: "\xE6\u01FD\u01E3"
17314 chars: "\uA739\uA73B"
17320 chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182"
17323 chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\xE7\u1E09\u0188\u023C\uA73F\u2184"
17326 chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA"
17332 chars: "\u01F3\u01C6"
17335 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"
17338 chars: "\u24D5\uFF46\u1E1F\u0192"
17356 chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79"
17359 chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265"
17365 chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131"
17368 chars: "\u24D9\uFF4A\u0135\u01F0\u0249"
17371 chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3"
17374 chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D"
17380 chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F"
17383 chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509"
17389 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"
17404 chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1"
17407 chars: "\u24E0\uFF51\u024B\uA757\uA759"
17410 chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783"
17413 chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282"
17419 chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787"
17428 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"
17431 chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C"
17437 chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73"
17440 chars: "\u24E7\uFF58\u1E8B\u1E8D"
17443 chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF"
17446 chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763"
17448 var diacriticsMap = {};
17450 for (var i = 0; i < replacementList.length; i += 1) {
17451 var chars = replacementList[i].chars;
17453 for (var j$1 = 0; j$1 < chars.length; j$1 += 1) {
17454 diacriticsMap[chars[j$1]] = replacementList[i].base;
17458 function removeDiacritics(str) {
17459 return str.replace(/[^\u0000-\u007e]/g, function (c) {
17460 return diacriticsMap[c] || c;
17464 var replacementList_1 = replacementList;
17465 var diacriticsMap_1 = diacriticsMap;
17468 replacementList: replacementList_1,
17469 diacriticsMap: diacriticsMap_1
17472 var isArabic_1 = createCommonjsModule(function (module, exports) {
17474 Object.defineProperty(exports, "__esModule", {
17477 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
17480 function isArabic(_char) {
17481 if (_char.length > 1) {
17482 // allow the newer chars?
17483 throw new Error('isArabic works on only one-character strings');
17486 var code = _char.charCodeAt(0);
17488 for (var i = 0; i < arabicBlocks.length; i++) {
17489 var block = arabicBlocks[i];
17491 if (code >= block[0] && code <= block[1]) {
17499 exports.isArabic = isArabic;
17501 function isMath(_char2) {
17502 if (_char2.length > 2) {
17503 // allow the newer chars?
17504 throw new Error('isMath works on only one-character strings');
17507 var code = _char2.charCodeAt(0);
17509 return code >= 0x660 && code <= 0x66C || code >= 0x6F0 && code <= 0x6F9;
17512 exports.isMath = isMath;
17515 var unicodeArabic = createCommonjsModule(function (module, exports) {
17517 Object.defineProperty(exports, "__esModule", {
17520 var arabicReference = {
17522 "normal": ["\u0627"],
17524 "normal": ["\u0627\u0653", "\u0622"],
17525 "isolated": "\uFE81",
17529 "normal": ["\u0627\u0654", "\u0623"],
17530 "isolated": "\uFE83",
17534 "normal": ["\u0627\u0655", "\u0625"],
17535 "isolated": "\uFE87",
17539 "normal": "\u0671",
17540 "isolated": "\uFB50",
17543 "wavy_hamza_above": ["\u0672"],
17544 "wavy_hamza_below": ["\u0627\u065F", "\u0673"],
17545 "high_hamza": ["\u0675", "\u0627\u0674"],
17546 "indic_two_above": ["\u0773"],
17547 "indic_three_above": ["\u0774"],
17549 "normal": ["\u0627\u064B"],
17551 "isolated": "\uFD3D"
17553 "isolated": "\uFE8D",
17557 "normal": ["\u0628"],
17558 "dotless": ["\u066E"],
17559 "three_dots_horizontally_below": ["\u0750"],
17560 "dot_below_three_dots_above": ["\u0751"],
17561 "three_dots_pointing_upwards_below": ["\u0752"],
17562 "three_dots_pointing_upwards_below_two_dots_above": ["\u0753"],
17563 "two_dots_below_dot_above": ["\u0754"],
17564 "inverted_small_v_below": ["\u0755"],
17565 "small_v": ["\u0756"],
17566 "small_v_below": ["\u08A0"],
17567 "hamza_above": ["\u08A1"],
17568 "small_meem_above": ["\u08B6"],
17569 "isolated": "\uFE8F",
17571 "initial": "\uFE91",
17575 "normal": ["\u0629"],
17576 "isolated": "\uFE93",
17580 "normal": ["\u062A"],
17581 "ring": ["\u067C"],
17582 "three_dots_above_downwards": ["\u067D"],
17583 "small_teh_above": ["\u08B8"],
17584 "isolated": "\uFE95",
17586 "initial": "\uFE97",
17590 "normal": ["\u062B"],
17591 "isolated": "\uFE99",
17593 "initial": "\uFE9B",
17597 "normal": ["\u062C"],
17598 "two_dots_above": ["\u08A2"],
17599 "isolated": "\uFE9D",
17601 "initial": "\uFE9F",
17605 "normal": ["\u062D"],
17606 "hamza_above": ["\u0681"],
17607 "two_dots_vertical_above": ["\u0682"],
17608 "three_dots_above": ["\u0685"],
17609 "two_dots_above": ["\u0757"],
17610 "three_dots_pointing_upwards_below": ["\u0758"],
17611 "small_tah_below": ["\u076E"],
17612 "small_tah_two_dots": ["\u076F"],
17613 "small_tah_above": ["\u0772"],
17614 "indic_four_below": ["\u077C"],
17615 "isolated": "\uFEA1",
17617 "initial": "\uFEA3",
17621 "normal": ["\u062E"],
17622 "isolated": "\uFEA5",
17624 "initial": "\uFEA7",
17628 "normal": ["\u062F"],
17629 "ring": ["\u0689"],
17630 "dot_below": ["\u068A"],
17631 "dot_below_small_tah": ["\u068B"],
17632 "three_dots_above_downwards": ["\u068F"],
17633 "four_dots_above": ["\u0690"],
17634 "inverted_v": ["\u06EE"],
17635 "two_dots_vertically_below_small_tah": ["\u0759"],
17636 "inverted_small_v_below": ["\u075A"],
17637 "three_dots_below": ["\u08AE"],
17638 "isolated": "\uFEA9",
17642 "normal": ["\u0630"],
17643 "isolated": "\uFEAB",
17647 "normal": ["\u0631"],
17648 "small_v": ["\u0692"],
17649 "ring": ["\u0693"],
17650 "dot_below": ["\u0694"],
17651 "small_v_below": ["\u0695"],
17652 "dot_below_dot_above": ["\u0696"],
17653 "two_dots_above": ["\u0697"],
17654 "four_dots_above": ["\u0699"],
17655 "inverted_v": ["\u06EF"],
17656 "stroke": ["\u075B"],
17657 "two_dots_vertically_above": ["\u076B"],
17658 "hamza_above": ["\u076C"],
17659 "small_tah_two_dots": ["\u0771"],
17660 "loop": ["\u08AA"],
17661 "small_noon_above": ["\u08B9"],
17662 "isolated": "\uFEAD",
17666 "normal": ["\u0632"],
17667 "inverted_v_above": ["\u08B2"],
17668 "isolated": "\uFEAF",
17672 "normal": ["\u0633"],
17673 "dot_below_dot_above": ["\u069A"],
17674 "three_dots_below": ["\u069B"],
17675 "three_dots_below_three_dots_above": ["\u069C"],
17676 "four_dots_above": ["\u075C"],
17677 "two_dots_vertically_above": ["\u076D"],
17678 "small_tah_two_dots": ["\u0770"],
17679 "indic_four_above": ["\u077D"],
17680 "inverted_v": ["\u077E"],
17681 "isolated": "\uFEB1",
17683 "initial": "\uFEB3",
17687 "normal": ["\u0634"],
17688 "dot_below": ["\u06FA"],
17689 "isolated": "\uFEB5",
17691 "initial": "\uFEB7",
17695 "normal": ["\u0635"],
17696 "two_dots_below": ["\u069D"],
17697 "three_dots_above": ["\u069E"],
17698 "three_dots_below": ["\u08AF"],
17699 "isolated": "\uFEB9",
17701 "initial": "\uFEBB",
17705 "normal": ["\u0636"],
17706 "dot_below": ["\u06FB"],
17707 "isolated": "\uFEBD",
17709 "initial": "\uFEBF",
17713 "normal": ["\u0637"],
17714 "three_dots_above": ["\u069F"],
17715 "two_dots_above": ["\u08A3"],
17716 "isolated": "\uFEC1",
17718 "initial": "\uFEC3",
17722 "normal": ["\u0638"],
17723 "isolated": "\uFEC5",
17725 "initial": "\uFEC7",
17729 "normal": ["\u0639"],
17730 "three_dots_above": ["\u06A0"],
17731 "two_dots_above": ["\u075D"],
17732 "three_dots_pointing_downwards_above": ["\u075E"],
17733 "two_dots_vertically_above": ["\u075F"],
17734 "three_dots_below": ["\u08B3"],
17735 "isolated": "\uFEC9",
17737 "initial": "\uFECB",
17741 "normal": ["\u063A"],
17742 "dot_below": ["\u06FC"],
17743 "isolated": "\uFECD",
17745 "initial": "\uFECF",
17749 "normal": ["\u0641"],
17750 "dotless": ["\u06A1"],
17751 "dot_moved_below": ["\u06A2"],
17752 "dot_below": ["\u06A3"],
17753 "three_dots_below": ["\u06A5"],
17754 "two_dots_below": ["\u0760"],
17755 "three_dots_pointing_upwards_below": ["\u0761"],
17756 "dot_below_three_dots_above": ["\u08A4"],
17757 "isolated": "\uFED1",
17759 "initial": "\uFED3",
17763 "normal": ["\u0642"],
17764 "dotless": ["\u066F"],
17765 "dot_above": ["\u06A7"],
17766 "three_dots_above": ["\u06A8"],
17767 "dot_below": ["\u08A5"],
17768 "isolated": "\uFED5",
17770 "initial": "\uFED7",
17774 "normal": ["\u0643"],
17775 "swash": ["\u06AA"],
17776 "ring": ["\u06AB"],
17777 "dot_above": ["\u06AC"],
17778 "three_dots_below": ["\u06AE"],
17779 "two_dots_above": ["\u077F"],
17780 "dot_below": ["\u08B4"],
17781 "isolated": "\uFED9",
17783 "initial": "\uFEDB",
17787 "normal": ["\u0644"],
17788 "small_v": ["\u06B5"],
17789 "dot_above": ["\u06B6"],
17790 "three_dots_above": ["\u06B7"],
17791 "three_dots_below": ["\u06B8"],
17793 "double_bar": ["\u08A6"],
17794 "isolated": "\uFEDD",
17796 "initial": "\uFEDF",
17800 "normal": ["\u0645"],
17801 "dot_above": ["\u0765"],
17802 "dot_below": ["\u0766"],
17803 "three_dots_above": ["\u08A7"],
17804 "isolated": "\uFEE1",
17806 "initial": "\uFEE3",
17810 "normal": ["\u0646"],
17811 "dot_below": ["\u06B9"],
17812 "ring": ["\u06BC"],
17813 "three_dots_above": ["\u06BD"],
17814 "two_dots_below": ["\u0767"],
17815 "small_tah": ["\u0768"],
17816 "small_v": ["\u0769"],
17817 "isolated": "\uFEE5",
17819 "initial": "\uFEE7",
17823 "normal": ["\u0647"],
17824 "isolated": "\uFEE9",
17826 "initial": "\uFEEB",
17830 "normal": ["\u0648"],
17832 "normal": ["\u0624", "\u0648\u0654"],
17833 "isolated": "\uFE85",
17836 "high_hamza": ["\u0676", "\u0648\u0674"],
17837 "ring": ["\u06C4"],
17838 "two_dots_above": ["\u06CA"],
17839 "dot_above": ["\u06CF"],
17840 "indic_two_above": ["\u0778"],
17841 "indic_three_above": ["\u0779"],
17842 "dot_within": ["\u08AB"],
17843 "isolated": "\uFEED",
17847 "normal": ["\u0649"],
17848 "hamza_above": ["\u0626", "\u064A\u0654"],
17849 "initial": "\uFBE8",
17850 "medial": "\uFBE9",
17851 "isolated": "\uFEEF",
17855 "normal": ["\u064A"],
17857 "normal": ["\u0626", "\u0649\u0654"],
17858 "isolated": "\uFE89",
17860 "initial": "\uFE8B",
17863 "two_dots_below_hamza_above": ["\u08A8"],
17864 "high_hamza": ["\u0678", "\u064A\u0674"],
17865 "tail": ["\u06CD"],
17866 "small_v": ["\u06CE"],
17867 "three_dots_below": ["\u06D1"],
17868 "two_dots_below_dot_above": ["\u08A9"],
17869 "two_dots_below_small_noon_above": ["\u08BA"],
17870 "isolated": "\uFEF1",
17872 "initial": "\uFEF3",
17876 "normal": ["\u0679"],
17877 "isolated": "\uFB66",
17879 "initial": "\uFB68",
17883 "normal": ["\u067A"],
17884 "isolated": "\uFB5E",
17886 "initial": "\uFB60",
17890 "normal": ["\u067B"],
17891 "isolated": "\uFB52",
17893 "initial": "\uFB54",
17897 "normal": ["\u067E"],
17898 "small_meem_above": ["\u08B7"],
17899 "isolated": "\uFB56",
17901 "initial": "\uFB58",
17905 "normal": ["\u067F"],
17906 "isolated": "\uFB62",
17908 "initial": "\uFB64",
17912 "normal": ["\u0680"],
17913 "isolated": "\uFB5A",
17915 "initial": "\uFB5C",
17919 "normal": ["\u0683"],
17920 "isolated": "\uFB76",
17922 "initial": "\uFB78",
17926 "normal": ["\u0684"],
17927 "isolated": "\uFB72",
17929 "initial": "\uFB74",
17933 "normal": ["\u0686"],
17934 "dot_above": ["\u06BF"],
17935 "isolated": "\uFB7A",
17937 "initial": "\uFB7C",
17941 "normal": ["\u0687"],
17942 "isolated": "\uFB7E",
17944 "initial": "\uFB80",
17948 "normal": ["\u0688"],
17949 "isolated": "\uFB88",
17953 "normal": ["\u068C"],
17954 "isolated": "\uFB84",
17958 "normal": ["\u068D"],
17959 "isolated": "\uFB82",
17963 "normal": ["\u068F", "\u068E"],
17964 "isolated": "\uFB86",
17968 "normal": ["\u0691"],
17969 "isolated": "\uFB8C",
17973 "normal": ["\u0698"],
17974 "isolated": "\uFB8A",
17978 "normal": ["\u06A4"],
17979 "isolated": "\uFB6A",
17981 "initial": "\uFB6C",
17985 "normal": ["\u06A6"],
17986 "isolated": "\uFB6E",
17988 "initial": "\uFB70",
17992 "normal": ["\u06A9"],
17993 "dot_above": ["\u0762"],
17994 "three_dots_above": ["\u0763"],
17995 "three_dots_pointing_upwards_below": ["\u0764"],
17996 "isolated": "\uFB8E",
17998 "initial": "\uFB90",
18002 "normal": ["\u06AD"],
18003 "isolated": "\uFBD3",
18005 "initial": "\uFBD5",
18009 "normal": ["\u06AF"],
18010 "ring": ["\u06B0"],
18011 "two_dots_below": ["\u06B2"],
18012 "three_dots_above": ["\u06B4"],
18013 "inverted_stroke": ["\u08B0"],
18014 "isolated": "\uFB92",
18016 "initial": "\uFB94",
18020 "normal": ["\u06B1"],
18021 "isolated": "\uFB9A",
18023 "initial": "\uFB9C",
18027 "normal": ["\u06B3"],
18028 "isolated": "\uFB96",
18030 "initial": "\uFB98",
18034 "normal": ["\u06BA"],
18035 "isolated": "\uFB9E",
18039 "normal": ["\u06BB"],
18040 "isolated": "\uFBA0",
18042 "initial": "\uFBA2",
18045 "heh doachashmee": {
18046 "normal": ["\u06BE"],
18047 "isolated": "\uFBAA",
18049 "initial": "\uFBAC",
18053 "normal": ["\u06C1"],
18054 "hamza_above": ["\u06C1\u0654", "\u06C2"],
18055 "isolated": "\uFBA6",
18057 "initial": "\uFBA8",
18060 "teh marbuta goal": {
18061 "normal": ["\u06C3"]
18064 "normal": ["\u06C5"],
18065 "isolated": "\uFBE0",
18069 "normal": ["\u06C6"],
18070 "isolated": "\uFBD9",
18074 "normal": ["\u06C7"],
18076 "normal": ["\u0677", "\u06C7\u0674"],
18077 "isolated": "\uFBDD"
18079 "isolated": "\uFBD7",
18083 "normal": ["\u06C8"],
18084 "isolated": "\uFBDB",
18088 "normal": ["\u06C9"],
18089 "isolated": "\uFBE2",
18093 "normal": ["\u06CB"],
18094 "isolated": "\uFBDE",
18098 "normal": ["\u06CC"],
18099 "indic_two_above": ["\u0775"],
18100 "indic_three_above": ["\u0776"],
18101 "indic_four_above": ["\u0777"],
18102 "isolated": "\uFBFC",
18104 "initial": "\uFBFE",
18108 "normal": ["\u06D0"],
18109 "isolated": "\uFBE4",
18111 "initial": "\uFBE6",
18115 "normal": ["\u06D2"],
18117 "normal": ["\u06D2\u0654", "\u06D3"],
18118 "isolated": "\uFBB0",
18121 "indic_two_above": ["\u077A"],
18122 "indic_three_above": ["\u077B"],
18123 "isolated": "\uFBAE",
18127 "normal": ["\u06D5"],
18128 "isolated": "\u06D5",
18131 "normal": ["\u06C0", "\u06D5\u0654"],
18132 "isolated": "\uFBA4",
18137 "normal": ["\u08AC"]
18140 "normal": ["\u08AD"]
18143 "normal": ["\u08B1"]
18146 "normal": ["\u08BB"]
18149 "normal": ["\u08BC"]
18152 "normal": ["\u08BD"]
18155 exports["default"] = arabicReference;
18158 var unicodeLigatures = createCommonjsModule(function (module, exports) {
18160 Object.defineProperty(exports, "__esModule", {
18163 var ligatureReference = {
18165 "isolated": "\uFBEA",
18169 "isolated": "\uFBEC",
18173 "isolated": "\uFBEE",
18177 "isolated": "\uFBF0",
18181 "isolated": "\uFBF2",
18185 "isolated": "\uFBF4",
18189 "isolated": "\uFBF6",
18191 "initial": "\uFBF8"
18194 "uighur_kirghiz": {
18195 "isolated": "\uFBF9",
18197 "initial": "\uFBFB"
18199 "isolated": "\uFC03",
18203 "isolated": "\uFC00",
18204 "initial": "\uFC97"
18207 "isolated": "\uFC01",
18208 "initial": "\uFC98"
18211 "isolated": "\uFC02",
18213 "initial": "\uFC9A",
18217 "isolated": "\uFC04",
18221 "isolated": "\uFC05",
18222 "initial": "\uFC9C"
18225 "isolated": "\uFC06",
18226 "initial": "\uFC9D"
18229 "isolated": "\uFC07",
18230 "initial": "\uFC9E"
18233 "isolated": "\uFC08",
18235 "initial": "\uFC9F",
18239 "isolated": "\uFC09",
18243 "isolated": "\uFC0A",
18247 "isolated": "\uFC0B",
18248 "initial": "\uFCA1"
18251 "isolated": "\uFC0C",
18252 "initial": "\uFCA2"
18255 "isolated": "\uFC0D",
18256 "initial": "\uFCA3"
18259 "isolated": "\uFC0E",
18261 "initial": "\uFCA4",
18265 "isolated": "\uFC0F",
18269 "isolated": "\uFC10",
18273 "isolated": "\uFC11"
18276 "isolated": "\uFC12",
18278 "initial": "\uFCA6",
18282 "isolated": "\uFC13",
18286 "isolated": "\uFC14"
18289 "isolated": "\uFC15",
18290 "initial": "\uFCA7"
18293 "isolated": "\uFC16",
18294 "initial": "\uFCA8"
18297 "isolated": "\uFC17",
18298 "initial": "\uFCA9"
18301 "isolated": "\uFC18",
18302 "initial": "\uFCAA"
18305 "isolated": "\uFC19",
18306 "initial": "\uFCAB"
18309 "isolated": "\uFC1A"
18312 "isolated": "\uFC1B",
18313 "initial": "\uFCAC"
18316 "isolated": "\uFC1C",
18317 "initial": "\uFCAD",
18321 "isolated": "\uFC1D",
18322 "initial": "\uFCAE",
18326 "isolated": "\uFC1E",
18327 "initial": "\uFCAF",
18331 "isolated": "\uFC1F",
18332 "initial": "\uFCB0",
18336 "isolated": "\uFC20",
18337 "initial": "\uFCB1"
18340 "isolated": "\uFC21",
18341 "initial": "\uFCB3"
18344 "isolated": "\uFC22",
18345 "initial": "\uFCB4"
18348 "isolated": "\uFC23",
18349 "initial": "\uFCB5"
18352 "isolated": "\uFC24",
18353 "initial": "\uFCB6"
18356 "isolated": "\uFC25",
18357 "initial": "\uFCB7"
18360 "isolated": "\uFC26",
18361 "initial": "\uFCB8"
18364 "isolated": "\uFC27",
18365 "initial": "\uFD33",
18369 "isolated": "\uFC28",
18370 "initial": "\uFCB9",
18374 "isolated": "\uFC29",
18375 "initial": "\uFCBA"
18378 "isolated": "\uFC2A",
18379 "initial": "\uFCBB"
18382 "isolated": "\uFC2B",
18383 "initial": "\uFCBC"
18386 "isolated": "\uFC2C",
18387 "initial": "\uFCBD"
18390 "isolated": "\uFC2D",
18391 "initial": "\uFCBE"
18394 "isolated": "\uFC2E",
18395 "initial": "\uFCBF"
18398 "isolated": "\uFC2F",
18399 "initial": "\uFCC0"
18402 "isolated": "\uFC30",
18403 "initial": "\uFCC1"
18406 "isolated": "\uFC31",
18410 "isolated": "\uFC32",
18414 "isolated": "\uFC33",
18415 "initial": "\uFCC2"
18418 "isolated": "\uFC34",
18419 "initial": "\uFCC3"
18422 "isolated": "\uFC35",
18426 "isolated": "\uFC36",
18430 "isolated": "\uFC37",
18434 "isolated": "\uFC38",
18435 "initial": "\uFCC4"
18438 "isolated": "\uFC39",
18439 "initial": "\uFCC5"
18442 "isolated": "\uFC3A",
18443 "initial": "\uFCC6"
18446 "isolated": "\uFC3B",
18448 "initial": "\uFCC7",
18452 "isolated": "\uFC3C",
18454 "initial": "\uFCC8",
18458 "isolated": "\uFC3D",
18462 "isolated": "\uFC3E",
18466 "isolated": "\uFC3F",
18467 "initial": "\uFCC9"
18470 "isolated": "\uFC40",
18471 "initial": "\uFCCA"
18474 "isolated": "\uFC41",
18475 "initial": "\uFCCB"
18478 "isolated": "\uFC42",
18480 "initial": "\uFCCC",
18484 "isolated": "\uFC43",
18488 "isolated": "\uFC44",
18492 "isolated": "\uFC45",
18493 "initial": "\uFCCE"
18496 "isolated": "\uFC46",
18497 "initial": "\uFCCF"
18500 "isolated": "\uFC47",
18501 "initial": "\uFCD0"
18504 "isolated": "\uFC48",
18506 "initial": "\uFCD1"
18509 "isolated": "\uFC49"
18512 "isolated": "\uFC4A"
18515 "isolated": "\uFC4B",
18516 "initial": "\uFCD2"
18519 "isolated": "\uFC4C",
18520 "initial": "\uFCD3"
18523 "isolated": "\uFC4D",
18524 "initial": "\uFCD4"
18527 "isolated": "\uFC4E",
18529 "initial": "\uFCD5",
18533 "isolated": "\uFC4F",
18537 "isolated": "\uFC50",
18541 "isolated": "\uFC51",
18542 "initial": "\uFCD7"
18545 "isolated": "\uFC52",
18546 "initial": "\uFCD8"
18549 "isolated": "\uFC53"
18552 "isolated": "\uFC54"
18555 "isolated": "\uFC55",
18556 "initial": "\uFCDA"
18559 "isolated": "\uFC56",
18560 "initial": "\uFCDB"
18563 "isolated": "\uFC57",
18564 "initial": "\uFCDC"
18567 "isolated": "\uFC58",
18569 "initial": "\uFCDD",
18573 "isolated": "\uFC59",
18577 "isolated": "\uFC5A",
18581 "isolated": "\uFC5B"
18584 "isolated": "\uFC5C"
18587 "isolated": "\uFC5D",
18591 "isolated": "\uFC5E"
18594 "isolated": "\uFC5F"
18597 "isolated": "\uFC60"
18600 "isolated": "\uFC61"
18603 "isolated": "\uFC62"
18606 "isolated": "\uFC63"
18669 "initial": "\uFC99"
18672 "initial": "\uFC9B",
18676 "initial": "\uFCA0",
18680 "initial": "\uFCA5",
18684 "initial": "\uFCB2"
18687 "initial": "\uFCCD"
18690 "initial": "\uFCD6",
18694 "initial": "\uFCD9"
18697 "initial": "\uFCDE",
18704 "medial": "\uFCE8",
18705 "initial": "\uFD31"
18708 "medial": "\uFCE9",
18709 "isolated": "\uFD0C",
18711 "initial": "\uFD30"
18714 "medial": "\uFCEA",
18715 "initial": "\uFD32"
18717 "\u0640\u064E\u0651": {
18720 "\u0640\u064F\u0651": {
18723 "\u0640\u0650\u0651": {
18727 "isolated": "\uFCF5",
18731 "isolated": "\uFCF6",
18735 "isolated": "\uFCF7",
18739 "isolated": "\uFCF8",
18743 "isolated": "\uFCF9",
18747 "isolated": "\uFCFA",
18751 "isolated": "\uFCFB"
18754 "isolated": "\uFCFC",
18758 "isolated": "\uFCFD",
18762 "isolated": "\uFCFE",
18766 "isolated": "\uFCFF",
18770 "isolated": "\uFD00",
18774 "isolated": "\uFD01",
18778 "isolated": "\uFD02",
18782 "isolated": "\uFD03",
18786 "isolated": "\uFD04",
18790 "isolated": "\uFD05",
18794 "isolated": "\uFD06",
18798 "isolated": "\uFD07",
18802 "isolated": "\uFD08",
18806 "isolated": "\uFD09",
18808 "initial": "\uFD2D",
18812 "isolated": "\uFD0A",
18814 "initial": "\uFD2E",
18818 "isolated": "\uFD0B",
18820 "initial": "\uFD2F",
18824 "isolated": "\uFD0D",
18828 "isolated": "\uFD0E",
18832 "isolated": "\uFD0F",
18836 "isolated": "\uFD10",
18842 "\u062A\u062C\u0645": {
18843 "initial": "\uFD50"
18845 "\u062A\u062D\u062C": {
18847 "initial": "\uFD52"
18849 "\u062A\u062D\u0645": {
18850 "initial": "\uFD53"
18852 "\u062A\u062E\u0645": {
18853 "initial": "\uFD54"
18855 "\u062A\u0645\u062C": {
18856 "initial": "\uFD55"
18858 "\u062A\u0645\u062D": {
18859 "initial": "\uFD56"
18861 "\u062A\u0645\u062E": {
18862 "initial": "\uFD57"
18864 "\u062C\u0645\u062D": {
18866 "initial": "\uFD59"
18868 "\u062D\u0645\u064A": {
18871 "\u062D\u0645\u0649": {
18874 "\u0633\u062D\u062C": {
18875 "initial": "\uFD5C"
18877 "\u0633\u062C\u062D": {
18878 "initial": "\uFD5D"
18880 "\u0633\u062C\u0649": {
18883 "\u0633\u0645\u062D": {
18885 "initial": "\uFD60"
18887 "\u0633\u0645\u062C": {
18888 "initial": "\uFD61"
18890 "\u0633\u0645\u0645": {
18892 "initial": "\uFD63"
18894 "\u0635\u062D\u062D": {
18896 "initial": "\uFD65"
18898 "\u0635\u0645\u0645": {
18900 "initial": "\uFDC5"
18902 "\u0634\u062D\u0645": {
18904 "initial": "\uFD68"
18906 "\u0634\u062C\u064A": {
18909 "\u0634\u0645\u062E": {
18911 "initial": "\uFD6B"
18913 "\u0634\u0645\u0645": {
18915 "initial": "\uFD6D"
18917 "\u0636\u062D\u0649": {
18920 "\u0636\u062E\u0645": {
18922 "initial": "\uFD70"
18924 "\u0636\u0645\u062D": {
18927 "\u0637\u0645\u062D": {
18928 "initial": "\uFD72"
18930 "\u0637\u0645\u0645": {
18931 "initial": "\uFD73"
18933 "\u0637\u0645\u064A": {
18936 "\u0639\u062C\u0645": {
18938 "initial": "\uFDC4"
18940 "\u0639\u0645\u0645": {
18942 "initial": "\uFD77"
18944 "\u0639\u0645\u0649": {
18947 "\u063A\u0645\u0645": {
18950 "\u063A\u0645\u064A": {
18953 "\u063A\u0645\u0649": {
18956 "\u0641\u062E\u0645": {
18958 "initial": "\uFD7D"
18960 "\u0642\u0645\u062D": {
18962 "initial": "\uFDB4"
18964 "\u0642\u0645\u0645": {
18967 "\u0644\u062D\u0645": {
18969 "initial": "\uFDB5"
18971 "\u0644\u062D\u064A": {
18974 "\u0644\u062D\u0649": {
18977 "\u0644\u062C\u062C": {
18978 "initial": "\uFD83",
18981 "\u0644\u062E\u0645": {
18983 "initial": "\uFD86"
18985 "\u0644\u0645\u062D": {
18987 "initial": "\uFD88"
18989 "\u0645\u062D\u062C": {
18990 "initial": "\uFD89"
18992 "\u0645\u062D\u0645": {
18993 "initial": "\uFD8A"
18995 "\u0645\u062D\u064A": {
18998 "\u0645\u062C\u062D": {
18999 "initial": "\uFD8C"
19001 "\u0645\u062C\u0645": {
19002 "initial": "\uFD8D"
19004 "\u0645\u062E\u062C": {
19005 "initial": "\uFD8E"
19007 "\u0645\u062E\u0645": {
19008 "initial": "\uFD8F"
19010 "\u0645\u062C\u062E": {
19011 "initial": "\uFD92"
19013 "\u0647\u0645\u062C": {
19014 "initial": "\uFD93"
19016 "\u0647\u0645\u0645": {
19017 "initial": "\uFD94"
19019 "\u0646\u062D\u0645": {
19020 "initial": "\uFD95"
19022 "\u0646\u062D\u0649": {
19025 "\u0646\u062C\u0645": {
19027 "initial": "\uFD98"
19029 "\u0646\u062C\u0649": {
19032 "\u0646\u0645\u064A": {
19035 "\u0646\u0645\u0649": {
19038 "\u064A\u0645\u0645": {
19040 "initial": "\uFD9D"
19042 "\u0628\u062E\u064A": {
19045 "\u062A\u062C\u064A": {
19048 "\u062A\u062C\u0649": {
19051 "\u062A\u062E\u064A": {
19054 "\u062A\u062E\u0649": {
19057 "\u062A\u0645\u064A": {
19060 "\u062A\u0645\u0649": {
19063 "\u062C\u0645\u064A": {
19066 "\u062C\u062D\u0649": {
19069 "\u062C\u0645\u0649": {
19072 "\u0633\u062E\u0649": {
19075 "\u0635\u062D\u064A": {
19078 "\u0634\u062D\u064A": {
19081 "\u0636\u062D\u064A": {
19084 "\u0644\u062C\u064A": {
19087 "\u0644\u0645\u064A": {
19090 "\u064A\u062D\u064A": {
19093 "\u064A\u062C\u064A": {
19096 "\u064A\u0645\u064A": {
19099 "\u0645\u0645\u064A": {
19102 "\u0642\u0645\u064A": {
19105 "\u0646\u062D\u064A": {
19108 "\u0639\u0645\u064A": {
19111 "\u0643\u0645\u064A": {
19114 "\u0646\u062C\u062D": {
19115 "initial": "\uFDB8",
19118 "\u0645\u062E\u064A": {
19121 "\u0644\u062C\u0645": {
19122 "initial": "\uFDBA",
19125 "\u0643\u0645\u0645": {
19127 "initial": "\uFDC3"
19129 "\u062C\u062D\u064A": {
19132 "\u062D\u062C\u064A": {
19135 "\u0645\u062C\u064A": {
19138 "\u0641\u0645\u064A": {
19141 "\u0628\u062D\u064A": {
19144 "\u0633\u062E\u064A": {
19147 "\u0646\u062C\u064A": {
19151 "isolated": "\uFEF5",
19155 "isolated": "\uFEF7",
19159 "isolated": "\uFEF9",
19163 "isolated": "\uFEFB",
19167 "\u0635\u0644\u06D2": "\uFDF0",
19168 "\u0642\u0644\u06D2": "\uFDF1",
19169 "\u0627\u0644\u0644\u0647": "\uFDF2",
19170 "\u0627\u0643\u0628\u0631": "\uFDF3",
19171 "\u0645\u062D\u0645\u062F": "\uFDF4",
19172 "\u0635\u0644\u0639\u0645": "\uFDF5",
19173 "\u0631\u0633\u0648\u0644": "\uFDF6",
19174 "\u0639\u0644\u064A\u0647": "\uFDF7",
19175 "\u0648\u0633\u0644\u0645": "\uFDF8",
19176 "\u0635\u0644\u0649": "\uFDF9",
19177 "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA",
19178 "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB",
19179 "\u0631\u06CC\u0627\u0644": "\uFDFC"
19182 exports["default"] = ligatureReference;
19185 var reference = createCommonjsModule(function (module, exports) {
19187 Object.defineProperty(exports, "__esModule", {
19190 var letterList = Object.keys(unicodeArabic["default"]);
19191 exports.letterList = letterList;
19192 var ligatureList = Object.keys(unicodeLigatures["default"]);
19193 exports.ligatureList = ligatureList;
19194 var ligatureWordList = Object.keys(unicodeLigatures["default"].words);
19195 exports.ligatureWordList = ligatureWordList;
19196 var lams = "\u0644\u06B5\u06B6\u06B7\u06B8";
19197 exports.lams = lams;
19198 var alefs = "\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774";
19199 exports.alefs = alefs; // for (var l = 1; l < lams.length; l++) {
19200 // console.log('-');
19201 // for (var a = 0; a < alefs.length; a++) {
19202 // console.log(a + ': ' + lams[l] + alefs[a]);
19206 var tashkeel = "\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8";
19207 exports.tashkeel = tashkeel;
19209 function addToTashkeel(start, finish) {
19210 for (var i = start; i <= finish; i++) {
19211 exports.tashkeel = tashkeel += String.fromCharCode(i);
19215 addToTashkeel(0x0610, 0x061A);
19216 addToTashkeel(0x064B, 0x065F);
19217 addToTashkeel(0x06D6, 0x06DC);
19218 addToTashkeel(0x06E0, 0x06E4);
19219 addToTashkeel(0x06EA, 0x06ED);
19220 addToTashkeel(0x08D3, 0x08E1);
19221 addToTashkeel(0x08E3, 0x08FF);
19222 addToTashkeel(0xFE70, 0xFE7F);
19223 var lineBreakers = "\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9";
19224 exports.lineBreakers = lineBreakers;
19226 function addToLineBreakers(start, finish) {
19227 for (var i = start; i <= finish; i++) {
19228 exports.lineBreakers = lineBreakers += String.fromCharCode(i);
19232 addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored
19234 addToLineBreakers(0x0621, 0x0625);
19235 addToLineBreakers(0x062F, 0x0632);
19236 addToLineBreakers(0x0660, 0x066D); // numerals, math
19238 addToLineBreakers(0x0671, 0x0677);
19239 addToLineBreakers(0x0688, 0x0699);
19240 addToLineBreakers(0x06C3, 0x06CB);
19241 addToLineBreakers(0x06D2, 0x06F9);
19242 addToLineBreakers(0x0759, 0x075B);
19243 addToLineBreakers(0x08AA, 0x08AE);
19244 addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do
19245 // Presentation Forms A includes diacritics but they are meant to stand alone
19247 addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do
19250 addToLineBreakers(0x10E60, 0x10E7F);
19251 addToLineBreakers(0x1EC70, 0x1ECBF);
19252 addToLineBreakers(0x1EE00, 0x1EEFF);
19255 var GlyphSplitter_1 = createCommonjsModule(function (module, exports) {
19257 Object.defineProperty(exports, "__esModule", {
19261 function GlyphSplitter(word) {
19263 var lastLetter = '';
19264 word.split('').forEach(function (letter) {
19265 if (isArabic_1.isArabic(letter)) {
19266 if (reference.tashkeel.indexOf(letter) > -1) {
19267 letters[letters.length - 1] += letter;
19268 } else if (lastLetter.length && (reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1 || reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0)) {
19270 letters[letters.length - 1] += letter;
19272 letters.push(letter);
19275 letters.push(letter);
19278 if (reference.tashkeel.indexOf(letter) === -1) {
19279 lastLetter = letter;
19285 exports.GlyphSplitter = GlyphSplitter;
19288 var BaselineSplitter_1 = createCommonjsModule(function (module, exports) {
19290 Object.defineProperty(exports, "__esModule", {
19294 function BaselineSplitter(word) {
19296 var lastLetter = '';
19297 word.split('').forEach(function (letter) {
19298 if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) {
19299 if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) {
19300 letters[letters.length - 1] += letter;
19301 } else if (reference.lineBreakers.indexOf(lastLetter) > -1) {
19302 letters.push(letter);
19304 letters[letters.length - 1] += letter;
19307 letters.push(letter);
19310 if (reference.tashkeel.indexOf(letter) === -1) {
19311 // don't allow tashkeel to hide line break
19312 lastLetter = letter;
19318 exports.BaselineSplitter = BaselineSplitter;
19321 var Normalization = createCommonjsModule(function (module, exports) {
19323 Object.defineProperty(exports, "__esModule", {
19327 function Normal(word, breakPresentationForm) {
19328 // default is to turn initial/isolated/medial/final presentation form to generic
19329 if (typeof breakPresentationForm === 'undefined') {
19330 breakPresentationForm = true;
19333 var returnable = '';
19334 word.split('').forEach(function (letter) {
19335 if (!isArabic_1.isArabic(letter)) {
19336 returnable += letter;
19340 for (var w = 0; w < reference.letterList.length; w++) {
19341 // ok so we are checking this potential lettertron
19342 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19343 var versions = Object.keys(letterForms);
19345 for (var v = 0; v < versions.length; v++) {
19346 var localVersion = letterForms[versions[v]];
19348 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19349 // look at this embedded object
19350 var embeddedForms = Object.keys(localVersion);
19352 for (var ef = 0; ef < embeddedForms.length; ef++) {
19353 var form = localVersion[embeddedForms[ef]];
19355 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19357 // console.log('embedded match');
19358 if (form === letter) {
19360 if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) {
19361 // replace presentation form
19362 // console.log('keeping normal form of the letter');
19363 if (_typeof(localVersion['normal']) === 'object') {
19364 returnable += localVersion['normal'][0];
19366 returnable += localVersion['normal'];
19370 } // console.log('keeping this letter');
19373 returnable += letter;
19375 } else if (_typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19377 returnable += form[0]; // console.log('added the first letter from the same array');
19383 } else if (localVersion === letter) {
19385 if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) {
19386 // replace presentation form
19387 // console.log('keeping normal form of the letter');
19388 if (_typeof(letterForms['normal']) === 'object') {
19389 returnable += letterForms['normal'][0];
19391 returnable += letterForms['normal'];
19395 } // console.log('keeping this letter');
19398 returnable += letter;
19400 } else if (_typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19402 returnable += localVersion[0]; // console.log('added the first letter from the same array');
19410 for (var v2 = 0; v2 < reference.ligatureList.length; v2++) {
19411 var normalForm = reference.ligatureList[v2];
19413 if (normalForm !== 'words') {
19414 var ligForms = Object.keys(unicodeLigatures["default"][normalForm]);
19416 for (var f = 0; f < ligForms.length; f++) {
19417 if (unicodeLigatures["default"][normalForm][ligForms[f]] === letter) {
19418 returnable += normalForm;
19423 } // try words ligatures
19426 for (var v3 = 0; v3 < reference.ligatureWordList.length; v3++) {
19427 var _normalForm = reference.ligatureWordList[v3];
19429 if (unicodeLigatures["default"].words[_normalForm] === letter) {
19430 returnable += _normalForm;
19435 returnable += letter; // console.log('kept the letter')
19440 exports.Normal = Normal;
19443 var CharShaper_1 = createCommonjsModule(function (module, exports) {
19445 Object.defineProperty(exports, "__esModule", {
19449 function CharShaper(letter, form) {
19450 if (!isArabic_1.isArabic(letter)) {
19452 throw new Error('Not Arabic');
19455 if (letter === "\u0621") {
19460 for (var w = 0; w < reference.letterList.length; w++) {
19461 // ok so we are checking this potential lettertron
19462 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19463 var versions = Object.keys(letterForms);
19465 for (var v = 0; v < versions.length; v++) {
19466 var localVersion = letterForms[versions[v]];
19468 if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19469 if (versions.indexOf(form) > -1) {
19470 return letterForms[form];
19472 } else if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19474 var embeddedVersions = Object.keys(localVersion);
19476 for (var ev = 0; ev < embeddedVersions.length; ev++) {
19477 if (localVersion[embeddedVersions[ev]] === letter || _typeof(localVersion[embeddedVersions[ev]]) === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1) {
19478 if (embeddedVersions.indexOf(form) > -1) {
19479 return localVersion[form];
19488 exports.CharShaper = CharShaper;
19491 var WordShaper_1 = createCommonjsModule(function (module, exports) {
19493 Object.defineProperty(exports, "__esModule", {
19497 function WordShaper(word) {
19498 var state = 'initial';
19501 for (var w = 0; w < word.length; w++) {
19502 var nextLetter = ' ';
19504 for (var nxw = w + 1; nxw < word.length; nxw++) {
19505 if (!isArabic_1.isArabic(word[nxw])) {
19509 if (reference.tashkeel.indexOf(word[nxw]) === -1) {
19510 nextLetter = word[nxw];
19515 if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) {
19516 // space or other non-Arabic
19519 } else if (reference.tashkeel.indexOf(word[w]) > -1) {
19520 // tashkeel - add without changing state
19522 } else if (nextLetter === ' ' || // last Arabic letter in this word
19523 reference.lineBreakers.indexOf(word[w]) > -1) {
19524 // the current letter is known to break lines
19525 output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final');
19527 } else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) {
19528 // LA letters - advance an additional letter after this
19529 output += unicodeLigatures["default"][word[w] + nextLetter][state === 'initial' ? 'isolated' : 'final'];
19531 while (word[w] !== nextLetter) {
19537 output += CharShaper_1.CharShaper(word[w], state);
19545 exports.WordShaper = WordShaper;
19548 var ParentLetter_1 = createCommonjsModule(function (module, exports) {
19550 Object.defineProperty(exports, "__esModule", {
19554 function ParentLetter(letter) {
19555 if (!isArabic_1.isArabic(letter)) {
19556 throw new Error('Not an Arabic letter');
19559 for (var w = 0; w < reference.letterList.length; w++) {
19560 // ok so we are checking this potential lettertron
19561 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19562 var versions = Object.keys(letterForms);
19564 for (var v = 0; v < versions.length; v++) {
19565 var localVersion = letterForms[versions[v]];
19567 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19568 // look at this embedded object
19569 var embeddedForms = Object.keys(localVersion);
19571 for (var ef = 0; ef < embeddedForms.length; ef++) {
19572 var form = localVersion[embeddedForms[ef]];
19574 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19576 return localVersion;
19579 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19581 return letterForms;
19589 exports.ParentLetter = ParentLetter;
19591 function GrandparentLetter(letter) {
19592 if (!isArabic_1.isArabic(letter)) {
19593 throw new Error('Not an Arabic letter');
19596 for (var w = 0; w < reference.letterList.length; w++) {
19597 // ok so we are checking this potential lettertron
19598 var letterForms = unicodeArabic["default"][reference.letterList[w]];
19599 var versions = Object.keys(letterForms);
19601 for (var v = 0; v < versions.length; v++) {
19602 var localVersion = letterForms[versions[v]];
19604 if (_typeof(localVersion) === 'object' && typeof localVersion.indexOf === 'undefined') {
19605 // look at this embedded object
19606 var embeddedForms = Object.keys(localVersion);
19608 for (var ef = 0; ef < embeddedForms.length; ef++) {
19609 var form = localVersion[embeddedForms[ef]];
19611 if (form === letter || _typeof(form) === 'object' && form.indexOf && form.indexOf(letter) > -1) {
19613 return letterForms;
19616 } else if (localVersion === letter || _typeof(localVersion) === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
19618 return letterForms;
19626 exports.GrandparentLetter = GrandparentLetter;
19629 var lib = createCommonjsModule(function (module, exports) {
19631 Object.defineProperty(exports, "__esModule", {
19634 exports.isArabic = isArabic_1.isArabic;
19635 exports.GlyphSplitter = GlyphSplitter_1.GlyphSplitter;
19636 exports.BaselineSplitter = BaselineSplitter_1.BaselineSplitter;
19637 exports.Normal = Normalization.Normal;
19638 exports.CharShaper = CharShaper_1.CharShaper;
19639 exports.WordShaper = WordShaper_1.WordShaper;
19640 exports.ParentLetter = ParentLetter_1.ParentLetter;
19641 exports.GrandparentLetter = ParentLetter_1.GrandparentLetter;
19644 var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/;
19645 function fixRTLTextForSvg(inputText) {
19648 var arabicRegex = /[\u0600-\u06FF]/g;
19649 var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g;
19650 var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g;
19651 var thaanaVowel = /[\u07A6-\u07B0]/;
19652 var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/; // Arabic word shaping
19654 if (arabicRegex.test(inputText)) {
19655 inputText = lib.WordShaper(inputText);
19658 for (var n = 0; n < inputText.length; n++) {
19659 var c = inputText[n];
19661 if (arabicMath.test(c)) {
19662 // Arabic numbers go LTR
19663 ret += rtlBuffer.reverse().join('');
19666 if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) {
19667 ret += rtlBuffer.reverse().join('');
19671 if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) {
19672 rtlBuffer[rtlBuffer.length - 1] += c;
19673 } else if (rtlRegex.test(c) // include Arabic presentation forms
19674 || c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023 || c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279) {
19676 } else if (c === ' ' && rtlBuffer.length) {
19677 // whitespace within RTL text
19678 rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
19680 // non-RTL character
19681 ret += rtlBuffer.reverse().join('') + c;
19687 ret += rtlBuffer.reverse().join('');
19691 var propertyIsEnumerable = objectPropertyIsEnumerable.f;
19693 // `Object.{ entries, values }` methods implementation
19694 var createMethod$5 = function (TO_ENTRIES) {
19695 return function (it) {
19696 var O = toIndexedObject(it);
19697 var keys = objectKeys(O);
19698 var length = keys.length;
19702 while (length > i) {
19704 if (!descriptors || propertyIsEnumerable.call(O, key)) {
19705 result.push(TO_ENTRIES ? [key, O[key]] : O[key]);
19712 var objectToArray = {
19713 // `Object.entries` method
19714 // https://tc39.github.io/ecma262/#sec-object.entries
19715 entries: createMethod$5(true),
19716 // `Object.values` method
19717 // https://tc39.github.io/ecma262/#sec-object.values
19718 values: createMethod$5(false)
19721 var $values = objectToArray.values;
19723 // `Object.values` method
19724 // https://tc39.github.io/ecma262/#sec-object.values
19725 _export({ target: 'Object', stat: true }, {
19726 values: function values(O) {
19731 // https://github.com/openstreetmap/iD/issues/772
19732 // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
19736 _storage = localStorage;
19737 } catch (e) {} // eslint-disable-line no-empty
19740 _storage = _storage || function () {
19743 getItem: function getItem(k) {
19746 setItem: function setItem(k, v) {
19749 removeItem: function removeItem(k) {
19750 return delete s[k];
19754 // corePreferences is an interface for persisting basic key-value strings
19755 // within and between iD sessions on the same site.
19759 function corePreferences(k, v) {
19761 if (arguments.length === 1) return _storage.getItem(k);else if (v === null) _storage.removeItem(k);else _storage.setItem(k, v);
19763 /* eslint-disable no-console */
19764 if (typeof console !== 'undefined') {
19765 console.error('localStorage quota exceeded');
19767 /* eslint-enable no-console */
19772 function responseText(response) {
19773 if (!response.ok) throw new Error(response.status + " " + response.statusText);
19774 return response.text();
19777 function d3_text (input, init) {
19778 return fetch(input, init).then(responseText);
19781 function responseJson(response) {
19782 if (!response.ok) throw new Error(response.status + " " + response.statusText);
19783 if (response.status === 204 || response.status === 205) return;
19784 return response.json();
19787 function d3_json (input, init) {
19788 return fetch(input, init).then(responseJson);
19791 function parser(type) {
19792 return function (input, init) {
19793 return d3_text(input, init).then(function (text) {
19794 return new DOMParser().parseFromString(text, type);
19799 var d3_xml = parser("application/xml");
19800 var svg = parser("image/svg+xml");
19802 var _mainFileFetcher = coreFileFetcher(); // singleton
19803 // coreFileFetcher asynchronously fetches data from JSON files
19806 function coreFileFetcher() {
19808 var _inflight = {};
19810 'address_formats': 'data/address_formats.min.json',
19811 'deprecated': 'data/deprecated.min.json',
19812 'discarded': 'data/discarded.min.json',
19813 'imagery': 'data/imagery.min.json',
19814 'intro_graph': 'data/intro_graph.min.json',
19815 'keepRight': 'data/keepRight.min.json',
19816 'languages': 'data/languages.min.json',
19817 'locales': 'data/locales.min.json',
19818 'nsi_brands': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/brands.min.json',
19819 'nsi_filters': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/filters.min.json',
19820 'oci_features': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/features.min.json',
19821 'oci_resources': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/resources.min.json',
19822 'preset_categories': 'data/preset_categories.min.json',
19823 'preset_defaults': 'data/preset_defaults.min.json',
19824 'preset_fields': 'data/preset_fields.min.json',
19825 'preset_presets': 'data/preset_presets.min.json',
19826 'phone_formats': 'data/phone_formats.min.json',
19827 'qa_data': 'data/qa_data.min.json',
19828 'shortcuts': 'data/shortcuts.min.json',
19829 'territory_languages': 'data/territory_languages.min.json',
19830 'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json'
19832 var _cachedData = {}; // expose the cache; useful for tests
19834 _this.cache = function () {
19835 return _cachedData;
19836 }; // Returns a Promise to fetch data
19837 // (resolved with the data if we have it already)
19840 _this.get = function (which) {
19841 if (_cachedData[which]) {
19842 return Promise.resolve(_cachedData[which]);
19845 var file = _fileMap[which];
19847 var url = file && _this.asset(file);
19850 return Promise.reject("Unknown data file for \"".concat(which, "\""));
19853 var prom = _inflight[url];
19856 _inflight[url] = prom = d3_json(url).then(function (result) {
19857 delete _inflight[url];
19860 throw new Error("No data loaded for \"".concat(which, "\""));
19863 _cachedData[which] = result;
19865 })["catch"](function (err) {
19866 delete _inflight[url];
19872 }; // Accessor for the file map
19875 _this.fileMap = function (val) {
19876 if (!arguments.length) return _fileMap;
19881 var _assetPath = '';
19883 _this.assetPath = function (val) {
19884 if (!arguments.length) return _assetPath;
19889 var _assetMap = {};
19891 _this.assetMap = function (val) {
19892 if (!arguments.length) return _assetMap;
19897 _this.asset = function (val) {
19898 if (/^http(s)?:\/\//i.test(val)) return val;
19899 var filename = _assetPath + val;
19900 return _assetMap[filename] || filename;
19906 var $findIndex$1 = arrayIteration.findIndex;
19910 var FIND_INDEX = 'findIndex';
19911 var SKIPS_HOLES$1 = true;
19913 var USES_TO_LENGTH$b = arrayMethodUsesToLength(FIND_INDEX);
19915 // Shouldn't skip holes
19916 if (FIND_INDEX in []) Array(1)[FIND_INDEX](function () { SKIPS_HOLES$1 = false; });
19918 // `Array.prototype.findIndex` method
19919 // https://tc39.github.io/ecma262/#sec-array.prototype.findindex
19920 _export({ target: 'Array', proto: true, forced: SKIPS_HOLES$1 || !USES_TO_LENGTH$b }, {
19921 findIndex: function findIndex(callbackfn /* , that = undefined */) {
19922 return $findIndex$1(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
19926 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
19927 addToUnscopables(FIND_INDEX);
19929 var $includes$1 = arrayIncludes.includes;
19933 var USES_TO_LENGTH$c = arrayMethodUsesToLength('indexOf', { ACCESSORS: true, 1: 0 });
19935 // `Array.prototype.includes` method
19936 // https://tc39.github.io/ecma262/#sec-array.prototype.includes
19937 _export({ target: 'Array', proto: true, forced: !USES_TO_LENGTH$c }, {
19938 includes: function includes(el /* , fromIndex = 0 */) {
19939 return $includes$1(this, el, arguments.length > 1 ? arguments[1] : undefined);
19943 // https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
19944 addToUnscopables('includes');
19946 var notARegexp = function (it) {
19947 if (isRegexp(it)) {
19948 throw TypeError("The method doesn't accept regular expressions");
19952 var MATCH$2 = wellKnownSymbol('match');
19954 var correctIsRegexpLogic = function (METHOD_NAME) {
19957 '/./'[METHOD_NAME](regexp);
19960 regexp[MATCH$2] = false;
19961 return '/./'[METHOD_NAME](regexp);
19962 } catch (f) { /* empty */ }
19966 // `String.prototype.includes` method
19967 // https://tc39.github.io/ecma262/#sec-string.prototype.includes
19968 _export({ target: 'String', proto: true, forced: !correctIsRegexpLogic('includes') }, {
19969 includes: function includes(searchString /* , position = 0 */) {
19970 return !!~String(requireObjectCoercible(this))
19971 .indexOf(notARegexp(searchString), arguments.length > 1 ? arguments[1] : undefined);
19977 function utilDetect(refresh) {
19978 if (_detected && !refresh) return _detected;
19980 var ua = navigator.userAgent;
19984 m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
19987 _detected.browser = m[1];
19988 _detected.version = m[2];
19991 if (!_detected.browser) {
19992 m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i); // IE11
19995 _detected.browser = 'msie';
19996 _detected.version = m[1];
20000 if (!_detected.browser) {
20001 m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i); // Opera 15+
20004 _detected.browser = 'Opera';
20005 _detected.version = m[2];
20009 if (!_detected.browser) {
20010 m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
20013 _detected.browser = m[1];
20014 _detected.version = m[2];
20015 m = ua.match(/version\/([\.\d]+)/i);
20016 if (m !== null) _detected.version = m[1];
20020 if (!_detected.browser) {
20021 _detected.browser = navigator.appName;
20022 _detected.version = navigator.appVersion;
20023 } // keep major.minor version only..
20026 _detected.version = _detected.version.split(/\W/).slice(0, 2).join('.'); // detect other browser capabilities
20027 // Legacy Opera has incomplete svg style support. See #715
20029 _detected.opera = _detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15;
20031 if (_detected.browser.toLowerCase() === 'msie') {
20032 _detected.ie = true;
20033 _detected.browser = 'Internet Explorer';
20034 _detected.support = parseFloat(_detected.version) >= 11;
20036 _detected.ie = false;
20037 _detected.support = true;
20040 _detected.filedrop = window.FileReader && 'ondrop' in window;
20041 _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
20042 _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
20045 if (/Win/.test(ua)) {
20046 _detected.os = 'win';
20047 _detected.platform = 'Windows';
20048 } else if (/Mac/.test(ua)) {
20049 _detected.os = 'mac';
20050 _detected.platform = 'Macintosh';
20051 } else if (/X11/.test(ua) || /Linux/.test(ua)) {
20052 _detected.os = 'linux';
20053 _detected.platform = 'Linux';
20055 _detected.os = 'win';
20056 _detected.platform = 'Unknown';
20059 _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) || // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
20060 // so assume any "mac" with multitouch is actually iOS
20061 navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1) && /WebKit/.test(ua) && !/Edge/.test(ua) && !window.MSStream;
20063 // An array of locales requested by the browser in priority order.
20065 _detected.browserLocales = Array.from(new Set( // remove duplicates
20066 [navigator.language].concat(navigator.languages || []).concat([// old property for backwards compatibility
20067 navigator.userLanguage]) // remove any undefined values
20068 .filter(Boolean)));
20071 var loc = window.top.location;
20072 var origin = loc.origin;
20075 // for unpatched IE11
20076 origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port : '');
20079 _detected.host = origin + loc.pathname;
20083 var getOwnPropertyNames$2 = objectGetOwnPropertyNames.f;
20084 var getOwnPropertyDescriptor$3 = objectGetOwnPropertyDescriptor.f;
20085 var defineProperty$a = objectDefineProperty.f;
20086 var trim$2 = stringTrim.trim;
20088 var NUMBER = 'Number';
20089 var NativeNumber = global_1[NUMBER];
20090 var NumberPrototype = NativeNumber.prototype;
20092 // Opera ~12 has broken Object#toString
20093 var BROKEN_CLASSOF = classofRaw(objectCreate(NumberPrototype)) == NUMBER;
20095 // `ToNumber` abstract operation
20096 // https://tc39.github.io/ecma262/#sec-tonumber
20097 var toNumber = function (argument) {
20098 var it = toPrimitive(argument, false);
20099 var first, third, radix, maxCode, digits, length, index, code;
20100 if (typeof it == 'string' && it.length > 2) {
20102 first = it.charCodeAt(0);
20103 if (first === 43 || first === 45) {
20104 third = it.charCodeAt(2);
20105 if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix
20106 } else if (first === 48) {
20107 switch (it.charCodeAt(1)) {
20108 case 66: case 98: radix = 2; maxCode = 49; break; // fast equal of /^0b[01]+$/i
20109 case 79: case 111: radix = 8; maxCode = 55; break; // fast equal of /^0o[0-7]+$/i
20110 default: return +it;
20112 digits = it.slice(2);
20113 length = digits.length;
20114 for (index = 0; index < length; index++) {
20115 code = digits.charCodeAt(index);
20116 // parseInt parses a string to a first unavailable symbol
20117 // but ToNumber should return NaN if a string contains unavailable symbols
20118 if (code < 48 || code > maxCode) return NaN;
20119 } return parseInt(digits, radix);
20124 // `Number` constructor
20125 // https://tc39.github.io/ecma262/#sec-number-constructor
20126 if (isForced_1(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1'))) {
20127 var NumberWrapper = function Number(value) {
20128 var it = arguments.length < 1 ? 0 : value;
20130 return dummy instanceof NumberWrapper
20131 // check on 1..constructor(foo) case
20132 && (BROKEN_CLASSOF ? fails(function () { NumberPrototype.valueOf.call(dummy); }) : classofRaw(dummy) != NUMBER)
20133 ? inheritIfRequired(new NativeNumber(toNumber(it)), dummy, NumberWrapper) : toNumber(it);
20135 for (var keys$3 = descriptors ? getOwnPropertyNames$2(NativeNumber) : (
20137 'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
20138 // ES2015 (in case, if modules with ES2015 Number statics required before):
20139 'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
20140 'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger'
20141 ).split(','), j$2 = 0, key$1; keys$3.length > j$2; j$2++) {
20142 if (has(NativeNumber, key$1 = keys$3[j$2]) && !has(NumberWrapper, key$1)) {
20143 defineProperty$a(NumberWrapper, key$1, getOwnPropertyDescriptor$3(NativeNumber, key$1));
20146 NumberWrapper.prototype = NumberPrototype;
20147 NumberPrototype.constructor = NumberWrapper;
20148 redefine(global_1, NUMBER, NumberWrapper);
20151 // `Number.MAX_SAFE_INTEGER` constant
20152 // https://tc39.github.io/ecma262/#sec-number.max_safe_integer
20153 _export({ target: 'Number', stat: true }, {
20154 MAX_SAFE_INTEGER: 0x1FFFFFFFFFFFFF
20157 var aesJs = createCommonjsModule(function (module, exports) {
20158 /*! MIT License. Copyright 2015-2018 Richard Moore <me@ricmoo.com>. See LICENSE.txt. */
20161 function checkInt(value) {
20162 return parseInt(value) === value;
20165 function checkInts(arrayish) {
20166 if (!checkInt(arrayish.length)) {
20170 for (var i = 0; i < arrayish.length; i++) {
20171 if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
20179 function coerceArray(arg, copy) {
20180 // ArrayBuffer view
20181 if (arg.buffer && arg.name === 'Uint8Array') {
20186 arg = Array.prototype.slice.call(arg);
20191 } // It's an array; check it is a valid representation of a byte
20194 if (Array.isArray(arg)) {
20195 if (!checkInts(arg)) {
20196 throw new Error('Array contains invalid value: ' + arg);
20199 return new Uint8Array(arg);
20200 } // Something else, but behaves like an array (maybe a Buffer? Arguments?)
20203 if (checkInt(arg.length) && checkInts(arg)) {
20204 return new Uint8Array(arg);
20207 throw new Error('unsupported array-like object');
20210 function createArray(length) {
20211 return new Uint8Array(length);
20214 function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
20215 if (sourceStart != null || sourceEnd != null) {
20216 if (sourceArray.slice) {
20217 sourceArray = sourceArray.slice(sourceStart, sourceEnd);
20219 sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
20223 targetArray.set(sourceArray, targetStart);
20226 var convertUtf8 = function () {
20227 function toBytes(text) {
20230 text = encodeURI(text);
20232 while (i < text.length) {
20233 var c = text.charCodeAt(i++); // if it is a % sign, encode the following 2 bytes as a hex value
20236 result.push(parseInt(text.substr(i, 2), 16));
20237 i += 2; // otherwise, just the actual byte
20243 return coerceArray(result);
20246 function fromBytes(bytes) {
20250 while (i < bytes.length) {
20254 result.push(String.fromCharCode(c));
20256 } else if (c > 191 && c < 224) {
20257 result.push(String.fromCharCode((c & 0x1f) << 6 | bytes[i + 1] & 0x3f));
20260 result.push(String.fromCharCode((c & 0x0f) << 12 | (bytes[i + 1] & 0x3f) << 6 | bytes[i + 2] & 0x3f));
20265 return result.join('');
20270 fromBytes: fromBytes
20274 var convertHex = function () {
20275 function toBytes(text) {
20278 for (var i = 0; i < text.length; i += 2) {
20279 result.push(parseInt(text.substr(i, 2), 16));
20283 } // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
20286 var Hex = '0123456789abcdef';
20288 function fromBytes(bytes) {
20291 for (var i = 0; i < bytes.length; i++) {
20293 result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
20296 return result.join('');
20301 fromBytes: fromBytes
20303 }(); // Number of rounds by keysize
20306 var numberOfRounds = {
20310 }; // Round constant words
20312 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)
20314 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];
20315 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
20317 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];
20318 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];
20319 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];
20320 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
20322 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];
20323 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];
20324 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];
20325 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
20327 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];
20328 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];
20329 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];
20330 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];
20332 function convertToInt32(bytes) {
20335 for (var i = 0; i < bytes.length; i += 4) {
20336 result.push(bytes[i] << 24 | bytes[i + 1] << 16 | bytes[i + 2] << 8 | bytes[i + 3]);
20342 var AES = function AES(key) {
20343 if (!(this instanceof AES)) {
20344 throw Error('AES must be instanitated with `new`');
20347 Object.defineProperty(this, 'key', {
20348 value: coerceArray(key, true)
20354 AES.prototype._prepare = function () {
20355 var rounds = numberOfRounds[this.key.length];
20357 if (rounds == null) {
20358 throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
20359 } // encryption round keys
20362 this._Ke = []; // decryption round keys
20366 for (var i = 0; i <= rounds; i++) {
20367 this._Ke.push([0, 0, 0, 0]);
20369 this._Kd.push([0, 0, 0, 0]);
20372 var roundKeyCount = (rounds + 1) * 4;
20373 var KC = this.key.length / 4; // convert the key into ints
20375 var tk = convertToInt32(this.key); // copy values into round key arrays
20379 for (var i = 0; i < KC; i++) {
20381 this._Ke[index][i % 4] = tk[i];
20382 this._Kd[rounds - index][i % 4] = tk[i];
20383 } // key expansion (fips-197 section 5.2)
20386 var rconpointer = 0;
20390 while (t < roundKeyCount) {
20392 tk[0] ^= S[tt >> 16 & 0xFF] << 24 ^ S[tt >> 8 & 0xFF] << 16 ^ S[tt & 0xFF] << 8 ^ S[tt >> 24 & 0xFF] ^ rcon[rconpointer] << 24;
20393 rconpointer += 1; // key expansion (for non-256 bit)
20396 for (var i = 1; i < KC; i++) {
20397 tk[i] ^= tk[i - 1];
20398 } // key expansion for 256-bit keys is "slightly different" (fips-197)
20401 for (var i = 1; i < KC / 2; i++) {
20402 tk[i] ^= tk[i - 1];
20405 tt = tk[KC / 2 - 1];
20406 tk[KC / 2] ^= S[tt & 0xFF] ^ S[tt >> 8 & 0xFF] << 8 ^ S[tt >> 16 & 0xFF] << 16 ^ S[tt >> 24 & 0xFF] << 24;
20408 for (var i = KC / 2 + 1; i < KC; i++) {
20409 tk[i] ^= tk[i - 1];
20411 } // copy values into round key arrays
20418 while (i < KC && t < roundKeyCount) {
20421 this._Ke[r][c] = tk[i];
20422 this._Kd[rounds - r][c] = tk[i++];
20425 } // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
20428 for (var r = 1; r < rounds; r++) {
20429 for (var c = 0; c < 4; c++) {
20430 tt = this._Kd[r][c];
20431 this._Kd[r][c] = U1[tt >> 24 & 0xFF] ^ U2[tt >> 16 & 0xFF] ^ U3[tt >> 8 & 0xFF] ^ U4[tt & 0xFF];
20436 AES.prototype.encrypt = function (plaintext) {
20437 if (plaintext.length != 16) {
20438 throw new Error('invalid plaintext size (must be 16 bytes)');
20441 var rounds = this._Ke.length - 1;
20442 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
20444 var t = convertToInt32(plaintext);
20446 for (var i = 0; i < 4; i++) {
20447 t[i] ^= this._Ke[0][i];
20448 } // apply round transforms
20451 for (var r = 1; r < rounds; r++) {
20452 for (var i = 0; i < 4; i++) {
20453 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];
20457 } // the last round is special
20460 var result = createArray(16),
20463 for (var i = 0; i < 4; i++) {
20464 tt = this._Ke[rounds][i];
20465 result[4 * i] = (S[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
20466 result[4 * i + 1] = (S[t[(i + 1) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
20467 result[4 * i + 2] = (S[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
20468 result[4 * i + 3] = (S[t[(i + 3) % 4] & 0xff] ^ tt) & 0xff;
20474 AES.prototype.decrypt = function (ciphertext) {
20475 if (ciphertext.length != 16) {
20476 throw new Error('invalid ciphertext size (must be 16 bytes)');
20479 var rounds = this._Kd.length - 1;
20480 var a = [0, 0, 0, 0]; // convert plaintext to (ints ^ key)
20482 var t = convertToInt32(ciphertext);
20484 for (var i = 0; i < 4; i++) {
20485 t[i] ^= this._Kd[0][i];
20486 } // apply round transforms
20489 for (var r = 1; r < rounds; r++) {
20490 for (var i = 0; i < 4; i++) {
20491 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];
20495 } // the last round is special
20498 var result = createArray(16),
20501 for (var i = 0; i < 4; i++) {
20502 tt = this._Kd[rounds][i];
20503 result[4 * i] = (Si[t[i] >> 24 & 0xff] ^ tt >> 24) & 0xff;
20504 result[4 * i + 1] = (Si[t[(i + 3) % 4] >> 16 & 0xff] ^ tt >> 16) & 0xff;
20505 result[4 * i + 2] = (Si[t[(i + 2) % 4] >> 8 & 0xff] ^ tt >> 8) & 0xff;
20506 result[4 * i + 3] = (Si[t[(i + 1) % 4] & 0xff] ^ tt) & 0xff;
20512 * Mode Of Operation - Electonic Codebook (ECB)
20516 var ModeOfOperationECB = function ModeOfOperationECB(key) {
20517 if (!(this instanceof ModeOfOperationECB)) {
20518 throw Error('AES must be instanitated with `new`');
20521 this.description = "Electronic Code Block";
20523 this._aes = new AES(key);
20526 ModeOfOperationECB.prototype.encrypt = function (plaintext) {
20527 plaintext = coerceArray(plaintext);
20529 if (plaintext.length % 16 !== 0) {
20530 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
20533 var ciphertext = createArray(plaintext.length);
20534 var block = createArray(16);
20536 for (var i = 0; i < plaintext.length; i += 16) {
20537 copyArray(plaintext, block, 0, i, i + 16);
20538 block = this._aes.encrypt(block);
20539 copyArray(block, ciphertext, i);
20545 ModeOfOperationECB.prototype.decrypt = function (ciphertext) {
20546 ciphertext = coerceArray(ciphertext);
20548 if (ciphertext.length % 16 !== 0) {
20549 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
20552 var plaintext = createArray(ciphertext.length);
20553 var block = createArray(16);
20555 for (var i = 0; i < ciphertext.length; i += 16) {
20556 copyArray(ciphertext, block, 0, i, i + 16);
20557 block = this._aes.decrypt(block);
20558 copyArray(block, plaintext, i);
20564 * Mode Of Operation - Cipher Block Chaining (CBC)
20568 var ModeOfOperationCBC = function ModeOfOperationCBC(key, iv) {
20569 if (!(this instanceof ModeOfOperationCBC)) {
20570 throw Error('AES must be instanitated with `new`');
20573 this.description = "Cipher Block Chaining";
20577 iv = createArray(16);
20578 } else if (iv.length != 16) {
20579 throw new Error('invalid initialation vector size (must be 16 bytes)');
20582 this._lastCipherblock = coerceArray(iv, true);
20583 this._aes = new AES(key);
20586 ModeOfOperationCBC.prototype.encrypt = function (plaintext) {
20587 plaintext = coerceArray(plaintext);
20589 if (plaintext.length % 16 !== 0) {
20590 throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
20593 var ciphertext = createArray(plaintext.length);
20594 var block = createArray(16);
20596 for (var i = 0; i < plaintext.length; i += 16) {
20597 copyArray(plaintext, block, 0, i, i + 16);
20599 for (var j = 0; j < 16; j++) {
20600 block[j] ^= this._lastCipherblock[j];
20603 this._lastCipherblock = this._aes.encrypt(block);
20604 copyArray(this._lastCipherblock, ciphertext, i);
20610 ModeOfOperationCBC.prototype.decrypt = function (ciphertext) {
20611 ciphertext = coerceArray(ciphertext);
20613 if (ciphertext.length % 16 !== 0) {
20614 throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
20617 var plaintext = createArray(ciphertext.length);
20618 var block = createArray(16);
20620 for (var i = 0; i < ciphertext.length; i += 16) {
20621 copyArray(ciphertext, block, 0, i, i + 16);
20622 block = this._aes.decrypt(block);
20624 for (var j = 0; j < 16; j++) {
20625 plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
20628 copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
20634 * Mode Of Operation - Cipher Feedback (CFB)
20638 var ModeOfOperationCFB = function ModeOfOperationCFB(key, iv, segmentSize) {
20639 if (!(this instanceof ModeOfOperationCFB)) {
20640 throw Error('AES must be instanitated with `new`');
20643 this.description = "Cipher Feedback";
20647 iv = createArray(16);
20648 } else if (iv.length != 16) {
20649 throw new Error('invalid initialation vector size (must be 16 size)');
20652 if (!segmentSize) {
20656 this.segmentSize = segmentSize;
20657 this._shiftRegister = coerceArray(iv, true);
20658 this._aes = new AES(key);
20661 ModeOfOperationCFB.prototype.encrypt = function (plaintext) {
20662 if (plaintext.length % this.segmentSize != 0) {
20663 throw new Error('invalid plaintext size (must be segmentSize bytes)');
20666 var encrypted = coerceArray(plaintext, true);
20669 for (var i = 0; i < encrypted.length; i += this.segmentSize) {
20670 xorSegment = this._aes.encrypt(this._shiftRegister);
20672 for (var j = 0; j < this.segmentSize; j++) {
20673 encrypted[i + j] ^= xorSegment[j];
20674 } // Shift the register
20677 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
20678 copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
20684 ModeOfOperationCFB.prototype.decrypt = function (ciphertext) {
20685 if (ciphertext.length % this.segmentSize != 0) {
20686 throw new Error('invalid ciphertext size (must be segmentSize bytes)');
20689 var plaintext = coerceArray(ciphertext, true);
20692 for (var i = 0; i < plaintext.length; i += this.segmentSize) {
20693 xorSegment = this._aes.encrypt(this._shiftRegister);
20695 for (var j = 0; j < this.segmentSize; j++) {
20696 plaintext[i + j] ^= xorSegment[j];
20697 } // Shift the register
20700 copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
20701 copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
20707 * Mode Of Operation - Output Feedback (OFB)
20711 var ModeOfOperationOFB = function ModeOfOperationOFB(key, iv) {
20712 if (!(this instanceof ModeOfOperationOFB)) {
20713 throw Error('AES must be instanitated with `new`');
20716 this.description = "Output Feedback";
20720 iv = createArray(16);
20721 } else if (iv.length != 16) {
20722 throw new Error('invalid initialation vector size (must be 16 bytes)');
20725 this._lastPrecipher = coerceArray(iv, true);
20726 this._lastPrecipherIndex = 16;
20727 this._aes = new AES(key);
20730 ModeOfOperationOFB.prototype.encrypt = function (plaintext) {
20731 var encrypted = coerceArray(plaintext, true);
20733 for (var i = 0; i < encrypted.length; i++) {
20734 if (this._lastPrecipherIndex === 16) {
20735 this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
20736 this._lastPrecipherIndex = 0;
20739 encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
20743 }; // Decryption is symetric
20746 ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
20748 * Counter object for CTR common mode of operation
20751 var Counter = function Counter(initialValue) {
20752 if (!(this instanceof Counter)) {
20753 throw Error('Counter must be instanitated with `new`');
20754 } // We allow 0, but anything false-ish uses the default 1
20757 if (initialValue !== 0 && !initialValue) {
20761 if (typeof initialValue === 'number') {
20762 this._counter = createArray(16);
20763 this.setValue(initialValue);
20765 this.setBytes(initialValue);
20769 Counter.prototype.setValue = function (value) {
20770 if (typeof value !== 'number' || parseInt(value) != value) {
20771 throw new Error('invalid counter value (must be an integer)');
20772 } // We cannot safely handle numbers beyond the safe range for integers
20775 if (value > Number.MAX_SAFE_INTEGER) {
20776 throw new Error('integer value out of safe range');
20779 for (var index = 15; index >= 0; --index) {
20780 this._counter[index] = value % 256;
20781 value = parseInt(value / 256);
20785 Counter.prototype.setBytes = function (bytes) {
20786 bytes = coerceArray(bytes, true);
20788 if (bytes.length != 16) {
20789 throw new Error('invalid counter bytes size (must be 16 bytes)');
20792 this._counter = bytes;
20795 Counter.prototype.increment = function () {
20796 for (var i = 15; i >= 0; i--) {
20797 if (this._counter[i] === 255) {
20798 this._counter[i] = 0;
20800 this._counter[i]++;
20806 * Mode Of Operation - Counter (CTR)
20810 var ModeOfOperationCTR = function ModeOfOperationCTR(key, counter) {
20811 if (!(this instanceof ModeOfOperationCTR)) {
20812 throw Error('AES must be instanitated with `new`');
20815 this.description = "Counter";
20818 if (!(counter instanceof Counter)) {
20819 counter = new Counter(counter);
20822 this._counter = counter;
20823 this._remainingCounter = null;
20824 this._remainingCounterIndex = 16;
20825 this._aes = new AES(key);
20828 ModeOfOperationCTR.prototype.encrypt = function (plaintext) {
20829 var encrypted = coerceArray(plaintext, true);
20831 for (var i = 0; i < encrypted.length; i++) {
20832 if (this._remainingCounterIndex === 16) {
20833 this._remainingCounter = this._aes.encrypt(this._counter._counter);
20834 this._remainingCounterIndex = 0;
20836 this._counter.increment();
20839 encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
20843 }; // Decryption is symetric
20846 ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt; ///////////////////////
20848 // See:https://tools.ietf.org/html/rfc2315
20850 function pkcs7pad(data) {
20851 data = coerceArray(data, true);
20852 var padder = 16 - data.length % 16;
20853 var result = createArray(data.length + padder);
20854 copyArray(data, result);
20856 for (var i = data.length; i < result.length; i++) {
20857 result[i] = padder;
20863 function pkcs7strip(data) {
20864 data = coerceArray(data, true);
20866 if (data.length < 16) {
20867 throw new Error('PKCS#7 invalid length');
20870 var padder = data[data.length - 1];
20873 throw new Error('PKCS#7 padding byte out of range');
20876 var length = data.length - padder;
20878 for (var i = 0; i < padder; i++) {
20879 if (data[length + i] !== padder) {
20880 throw new Error('PKCS#7 invalid padding byte');
20884 var result = createArray(length);
20885 copyArray(data, result, 0, 0, length);
20887 } ///////////////////////
20889 // The block cipher
20896 ecb: ModeOfOperationECB,
20897 cbc: ModeOfOperationCBC,
20898 cfb: ModeOfOperationCFB,
20899 ofb: ModeOfOperationOFB,
20900 ctr: ModeOfOperationCTR
20913 coerceArray: coerceArray,
20914 createArray: createArray,
20915 copyArray: copyArray
20920 module.exports = aesjs; // RequireJS/AMD
20921 // http://www.requirejs.org/docs/api.html
20922 // https://github.com/amdjs/amdjs-api/wiki/AMD
20927 // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes).
20928 // To generate a random key: window.crypto.getRandomValues(new Uint8Array(16));
20929 // This default signing key is built into iD and can be used to mask/unmask sensitive values.
20931 var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
20932 function utilAesEncrypt(text, key) {
20933 key = key || DEFAULT_128;
20934 var textBytes = aesJs.utils.utf8.toBytes(text);
20935 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
20936 var encryptedBytes = aesCtr.encrypt(textBytes);
20937 var encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes);
20938 return encryptedHex;
20940 function utilAesDecrypt(encryptedHex, key) {
20941 key = key || DEFAULT_128;
20942 var encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex);
20943 var aesCtr = new aesJs.ModeOfOperation.ctr(key);
20944 var decryptedBytes = aesCtr.decrypt(encryptedBytes);
20945 var text = aesJs.utils.utf8.fromBytes(decryptedBytes);
20949 function utilCleanTags(tags) {
20952 for (var k in tags) {
20956 if (v !== undefined) {
20957 out[k] = cleanValue(k, v);
20963 function cleanValue(k, v) {
20964 function keepSpaces(k) {
20965 return /_hours|_times|:conditional$/.test(k);
20969 return /^(description|note|fixme)$/.test(k);
20972 if (skip(k)) return v;
20973 var cleaned = v.split(';').map(function (s) {
20975 }).join(keepSpaces(k) ? '; ' : ';'); // The code below is not intended to validate websites and emails.
20976 // It is only intended to prevent obvious copy-paste errors. (#2323)
20977 // clean website- and email-like tags
20979 if (k.indexOf('website') !== -1 || k.indexOf('email') !== -1 || cleaned.indexOf('http') === 0) {
20980 cleaned = cleaned.replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars
20987 // Like selection.property('value', ...), but avoids no-op value sets,
20988 // which can result in layout/repaint thrashing in some situations.
20989 function utilGetSetValue(selection, value) {
20990 function d3_selection_value(value) {
20991 function valueNull() {
20995 function valueConstant() {
20996 if (this.value !== value) {
20997 this.value = value;
21001 function valueFunction() {
21002 var x = value.apply(this, arguments);
21004 if (x === null || x === undefined) {
21006 } else if (this.value !== x) {
21011 return value === null || value === undefined ? valueNull : typeof value === 'function' ? valueFunction : valueConstant;
21014 if (arguments.length === 1) {
21015 return selection.property('value');
21018 return selection.each(d3_selection_value(value));
21021 function utilKeybinding(namespace) {
21022 var _keybindings = {};
21024 function testBindings(d3_event, isCapturing) {
21025 var didMatch = false;
21026 var bindings = Object.keys(_keybindings).map(function (id) {
21027 return _keybindings[id];
21029 var i, binding; // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
21030 // so we don't strictly match on the shift key, but we prioritize
21031 // shifted keybindings first, and fallback to unshifted only if no match.
21032 // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
21033 // priority match shifted keybindings first
21035 for (i = 0; i < bindings.length; i++) {
21036 binding = bindings[i];
21037 if (!binding.event.modifiers.shiftKey) continue; // no shift
21039 if (!!binding.capture !== isCapturing) continue;
21041 if (matches(d3_event, binding, true)) {
21042 binding.callback(d3_event);
21043 didMatch = true; // match a max of one binding per event
21049 if (didMatch) return; // then unshifted keybindings
21051 for (i = 0; i < bindings.length; i++) {
21052 binding = bindings[i];
21053 if (binding.event.modifiers.shiftKey) continue; // shift
21055 if (!!binding.capture !== isCapturing) continue;
21057 if (matches(d3_event, binding, false)) {
21058 binding.callback(d3_event);
21063 function matches(d3_event, binding, testShift) {
21064 var event = d3_event;
21065 var isMatch = false;
21066 var tryKeyCode = true; // Prefer a match on `KeyboardEvent.key`
21068 if (event.key !== undefined) {
21069 tryKeyCode = event.key.charCodeAt(0) > 255; // outside ISO-Latin-1
21073 if (binding.event.key === undefined) {
21075 } else if (Array.isArray(binding.event.key)) {
21076 if (binding.event.key.map(function (s) {
21077 return s.toLowerCase();
21078 }).indexOf(event.key.toLowerCase()) === -1) isMatch = false;
21080 if (event.key.toLowerCase() !== binding.event.key.toLowerCase()) isMatch = false;
21082 } // Fallback match on `KeyboardEvent.keyCode`, can happen if:
21083 // - browser doesn't support `KeyboardEvent.key`
21084 // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?)
21087 if (!isMatch && tryKeyCode) {
21088 isMatch = event.keyCode === binding.event.keyCode;
21091 if (!isMatch) return false; // test modifier keys
21093 if (!(event.ctrlKey && event.altKey)) {
21094 // if both are set, assume AltGr and skip it - #4096
21095 if (event.ctrlKey !== binding.event.modifiers.ctrlKey) return false;
21096 if (event.altKey !== binding.event.modifiers.altKey) return false;
21099 if (event.metaKey !== binding.event.modifiers.metaKey) return false;
21100 if (testShift && event.shiftKey !== binding.event.modifiers.shiftKey) return false;
21105 function capture(d3_event) {
21106 testBindings(d3_event, true);
21109 function bubble(d3_event) {
21110 var tagName = select(d3_event.target).node().tagName;
21112 if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
21116 testBindings(d3_event, false);
21119 function keybinding(selection) {
21120 selection = selection || select(document);
21121 selection.on('keydown.capture.' + namespace, capture, true);
21122 selection.on('keydown.bubble.' + namespace, bubble, false);
21124 } // was: keybinding.off()
21127 keybinding.unbind = function (selection) {
21129 selection = selection || select(document);
21130 selection.on('keydown.capture.' + namespace, null);
21131 selection.on('keydown.bubble.' + namespace, null);
21135 keybinding.clear = function () {
21138 }; // Remove one or more keycode bindings.
21141 keybinding.off = function (codes, capture) {
21142 var arr = utilArrayUniq([].concat(codes));
21144 for (var i = 0; i < arr.length; i++) {
21145 var id = arr[i] + (capture ? '-capture' : '-bubble');
21146 delete _keybindings[id];
21150 }; // Add one or more keycode bindings.
21153 keybinding.on = function (codes, callback, capture) {
21154 if (typeof callback !== 'function') {
21155 return keybinding.off(codes, capture);
21158 var arr = utilArrayUniq([].concat(codes));
21160 for (var i = 0; i < arr.length; i++) {
21161 var id = arr[i] + (capture ? '-capture' : '-bubble');
21165 callback: callback,
21180 if (_keybindings[id]) {
21181 console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console
21184 _keybindings[id] = binding;
21185 var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
21187 for (var j = 0; j < matches.length; j++) {
21188 // Normalise matching errors
21189 if (matches[j] === '++') matches[j] = '+';
21191 if (matches[j] in utilKeybinding.modifierCodes) {
21192 var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]];
21193 binding.event.modifiers[prop] = true;
21195 binding.event.key = utilKeybinding.keys[matches[j]] || matches[j];
21197 if (matches[j] in utilKeybinding.keyCodes) {
21198 binding.event.keyCode = utilKeybinding.keyCodes[matches[j]];
21210 * See https://github.com/keithamus/jwerty
21213 utilKeybinding.modifierCodes = {
21217 // CTRL key, on Mac: ⌃
21220 // ALT key, on Mac: ⌥ (Alt)
21224 // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
21231 utilKeybinding.modifierProperties = {
21237 utilKeybinding.plusKeys = ['plus', 'ffplus', '=', 'ffequals', '≠', '±'];
21238 utilKeybinding.minusKeys = ['_', '-', 'ffminus', 'dash', '–', '—'];
21239 utilKeybinding.keys = {
21240 // Backspace key, on Mac: ⌫ (Backspace)
21242 backspace: 'Backspace',
21243 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
21256 'pause-break': 'Pause',
21257 // Caps Lock key, ⇪
21260 'caps-lock': 'CapsLock',
21261 // Escape key, on Mac: ⎋, on Windows: Esc
21262 '⎋': ['Escape', 'Esc'],
21263 escape: ['Escape', 'Esc'],
21264 esc: ['Escape', 'Esc'],
21266 space: [' ', 'Spacebar'],
21267 // Page-Up key, or pgup, on Mac: ↖
21270 'page-up': 'PageUp',
21271 // Page-Down key, or pgdown, on Mac: ↘
21273 pgdown: 'PageDown',
21274 'page-down': 'PageDown',
21275 // END key, on Mac: ⇟
21278 // HOME key, on Mac: ⇞
21281 // Insert key, or ins
21284 // Delete key, on Mac: ⌦ (Delete)
21285 '⌦': ['Delete', 'Del'],
21286 del: ['Delete', 'Del'],
21287 'delete': ['Delete', 'Del'],
21288 // Left Arrow Key, or ←
21289 '←': ['ArrowLeft', 'Left'],
21290 left: ['ArrowLeft', 'Left'],
21291 'arrow-left': ['ArrowLeft', 'Left'],
21292 // Up Arrow Key, or ↑
21293 '↑': ['ArrowUp', 'Up'],
21294 up: ['ArrowUp', 'Up'],
21295 'arrow-up': ['ArrowUp', 'Up'],
21296 // Right Arrow Key, or →
21297 '→': ['ArrowRight', 'Right'],
21298 right: ['ArrowRight', 'Right'],
21299 'arrow-right': ['ArrowRight', 'Right'],
21300 // Up Arrow Key, or ↓
21301 '↓': ['ArrowDown', 'Down'],
21302 down: ['ArrowDown', 'Down'],
21303 'arrow-down': ['ArrowDown', 'Down'],
21304 // odities, stuff for backward compatibility (browsers and code):
21305 // Num-Multiply, or *
21306 '*': ['*', 'Multiply'],
21307 star: ['*', 'Multiply'],
21308 asterisk: ['*', 'Multiply'],
21309 multiply: ['*', 'Multiply'],
21312 'plus': ['+', 'Add'],
21313 // Num-Subtract, or -
21314 '-': ['-', 'Subtract'],
21315 subtract: ['-', 'Subtract'],
21316 'dash': ['-', 'Subtract'],
21323 // Period, or ., or full-stop
21326 // Slash, or /, or forward-slash
21328 'forward-slash': '/',
21329 // Tick, or `, or back-quote
21332 // Open bracket, or [
21333 'open-bracket': '[',
21334 // Back slash, or \
21335 'back-slash': '\\',
21336 // Close backet, or ]
21337 'close-bracket': ']',
21338 // Apostrophe, or Quote, or '
21379 utilKeybinding.keyCodes = {
21380 // Backspace key, on Mac: ⌫ (Backspace)
21383 // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
21397 // Caps Lock key, ⇪
21401 // Escape key, on Mac: ⎋, on Windows: Esc
21407 // Page-Up key, or pgup, on Mac: ↖
21411 // Page-Down key, or pgdown, on Mac: ↘
21415 // END key, on Mac: ⇟
21418 // HOME key, on Mac: ⇞
21421 // Insert key, or ins
21424 // Delete key, on Mac: ⌦ (Delete)
21428 // Left Arrow Key, or ←
21432 // Up Arrow Key, or ↑
21436 // Right Arrow Key, or →
21440 // Up Arrow Key, or ↓
21444 // odities, printing characters that come out wrong:
21447 // Num-Multiply, or *
21455 // Num-Subtract, or -
21471 // Dash / Underscore key
21473 // Period, or ., or full-stop
21477 // Slash, or /, or forward-slash
21480 'forward-slash': 191,
21481 // Tick, or `, or back-quote
21485 // Open bracket, or [
21487 'open-bracket': 219,
21488 // Back slash, or \
21491 // Close backet, or ]
21493 'close-bracket': 221,
21494 // Apostrophe, or Quote, or '
21503 while (++i$1 < 106) {
21504 utilKeybinding.keyCodes['num-' + n] = i$1;
21512 while (++i$1 < 58) {
21513 utilKeybinding.keyCodes[n] = i$1;
21521 while (++i$1 < 136) {
21522 utilKeybinding.keyCodes['f' + n] = i$1;
21529 while (++i$1 < 91) {
21530 utilKeybinding.keyCodes[String.fromCharCode(i$1).toLowerCase()] = i$1;
21533 function utilObjectOmit(obj, omitKeys) {
21534 return Object.keys(obj).reduce(function (result, key) {
21535 if (omitKeys.indexOf(key) === -1) {
21536 result[key] = obj[key]; // keep
21543 // Copies a variable number of methods from source to target.
21544 function utilRebind(target, source) {
21546 n = arguments.length,
21550 target[method = arguments[i]] = d3_rebind(target, source, source[method]);
21554 } // Method is assumed to be a standard D3 getter-setter:
21555 // If passed with no arguments, gets the value.
21556 // If passed with arguments, sets the value and returns the target.
21558 function d3_rebind(target, source, method) {
21559 return function () {
21560 var value = method.apply(source, arguments);
21561 return value === source ? target : value;
21565 // A per-domain session mutex backed by a cookie and dead man's
21566 // switch. If the session crashes, the mutex will auto-release
21567 // after 5 seconds.
21568 // This accepts a string and returns an object that complies with utilSessionMutexType
21569 function utilSessionMutex(name) {
21574 var expires = new Date();
21575 expires.setSeconds(expires.getSeconds() + 5);
21576 document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict';
21579 mutex.lock = function () {
21580 if (intervalID) return true;
21581 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
21582 if (cookie) return false;
21584 intervalID = window.setInterval(renew, 4000);
21588 mutex.unlock = function () {
21589 if (!intervalID) return;
21590 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict';
21591 clearInterval(intervalID);
21595 mutex.locked = function () {
21596 return !!intervalID;
21602 function utilTiler() {
21603 var _size = [256, 256];
21605 var _tileSize = 256;
21606 var _zoomExtent = [0, 20];
21607 var _translate = [_size[0] / 2, _size[1] / 2];
21609 var _skipNullIsland = false;
21611 function clamp(num, min, max) {
21612 return Math.max(min, Math.min(num, max));
21615 function nearNullIsland(tile) {
21621 var center = Math.pow(2, z - 1);
21622 var width = Math.pow(2, z - 6);
21623 var min = center - width / 2;
21624 var max = center + width / 2 - 1;
21625 return x >= min && x <= max && y >= min && y <= max;
21632 var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
21633 var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]);
21635 var tileMax = Math.pow(2, z0) - 1;
21636 var log2ts = Math.log(_tileSize) * Math.LOG2E;
21637 var k = Math.pow(2, z - z0 + log2ts);
21638 var origin = [(_translate[0] - _scale / 2) / k, (_translate[1] - _scale / 2) / k];
21639 var cols = range(clamp(Math.floor(-origin[0]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[0] / k - origin[0]) + _margin, tileMin, tileMax + 1));
21640 var rows = range(clamp(Math.floor(-origin[1]) - _margin, tileMin, tileMax + 1), clamp(Math.ceil(_size[1] / k - origin[1]) + _margin, tileMin, tileMax + 1));
21643 for (var i = 0; i < rows.length; i++) {
21646 for (var j = 0; j < cols.length; j++) {
21649 if (i >= _margin && i <= rows.length - _margin && j >= _margin && j <= cols.length - _margin) {
21650 tiles.unshift([x, y, z0]); // tiles in view at beginning
21652 tiles.push([x, y, z0]); // tiles in margin at the end
21657 tiles.translate = origin;
21662 * getTiles() returns an array of tiles that cover the map view
21666 tiler.getTiles = function (projection) {
21667 var origin = [projection.scale() * Math.PI - projection.translate()[0], projection.scale() * Math.PI - projection.translate()[1]];
21668 this.size(projection.clipExtent()[1]).scale(projection.scale() * 2 * Math.PI).translate(projection.translate());
21669 var tiles = tiler();
21670 var ts = tiles.scale;
21671 return tiles.map(function (tile) {
21672 if (_skipNullIsland && nearNullIsland(tile)) {
21676 var x = tile[0] * ts - origin[0];
21677 var y = tile[1] * ts - origin[1];
21679 id: tile.toString(),
21681 extent: geoExtent(projection.invert([x, y + ts]), projection.invert([x + ts, y]))
21683 }).filter(Boolean);
21686 * getGeoJSON() returns a FeatureCollection for debugging tiles
21690 tiler.getGeoJSON = function (projection) {
21691 var features = tiler.getTiles(projection).map(function (tile) {
21700 coordinates: [tile.extent.polygon()]
21705 type: 'FeatureCollection',
21710 tiler.tileSize = function (val) {
21711 if (!arguments.length) return _tileSize;
21716 tiler.zoomExtent = function (val) {
21717 if (!arguments.length) return _zoomExtent;
21722 tiler.size = function (val) {
21723 if (!arguments.length) return _size;
21728 tiler.scale = function (val) {
21729 if (!arguments.length) return _scale;
21734 tiler.translate = function (val) {
21735 if (!arguments.length) return _translate;
21738 }; // number to extend the rows/columns beyond those covering the viewport
21741 tiler.margin = function (val) {
21742 if (!arguments.length) return _margin;
21747 tiler.skipNullIsland = function (val) {
21748 if (!arguments.length) return _skipNullIsland;
21749 _skipNullIsland = val;
21756 function utilTriggerEvent(target, type) {
21757 target.each(function () {
21758 var evt = document.createEvent('HTMLEvents');
21759 evt.initEvent(type, true, true);
21760 this.dispatchEvent(evt);
21764 var _mainLocalizer = coreLocalizer(); // singleton
21767 var _t = _mainLocalizer.t;
21768 // coreLocalizer manages language and locale parameters including translated strings
21771 function coreLocalizer() {
21772 var localizer = {};
21773 var _dataLanguages = {}; // `_dataLocales` is an object containing all _supported_ locale codes -> language info.
21774 // * `rtl` - right-to-left or left-to-right text direction
21775 // * `pct` - the percent of strings translated; 1 = 100%, full coverage
21778 // en: { rtl: false, pct: {…} },
21779 // de: { rtl: false, pct: {…} },
21783 var _dataLocales = {}; // `localeStrings` is an object containing all _loaded_ locale codes -> string data.
21785 // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
21786 // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
21790 var _localeStrings = {}; // the current locale
21792 var _localeCode = 'en-US'; // `_localeCodes` must contain `_localeCode` first, optionally followed by fallbacks
21794 var _localeCodes = ['en-US', 'en'];
21795 var _languageCode = 'en';
21796 var _textDirection = 'ltr';
21797 var _usesMetric = false;
21798 var _languageNames = {};
21799 var _scriptNames = {}; // getters for the current locale parameters
21801 localizer.localeCode = function () {
21802 return _localeCode;
21805 localizer.localeCodes = function () {
21806 return _localeCodes;
21809 localizer.languageCode = function () {
21810 return _languageCode;
21813 localizer.textDirection = function () {
21814 return _textDirection;
21817 localizer.usesMetric = function () {
21818 return _usesMetric;
21821 localizer.languageNames = function () {
21822 return _languageNames;
21825 localizer.scriptNames = function () {
21826 return _scriptNames;
21827 }; // The client app may want to manually set the locale, regardless of the
21828 // settings provided by the browser
21831 var _preferredLocaleCodes = [];
21833 localizer.preferredLocaleCodes = function (codes) {
21834 if (!arguments.length) return _preferredLocaleCodes;
21836 if (typeof codes === 'string') {
21837 // be generous and accept delimited strings as input
21838 _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean);
21840 _preferredLocaleCodes = codes;
21848 localizer.ensureLoaded = function () {
21849 if (_loadPromise) return _loadPromise;
21850 return _loadPromise = Promise.all([// load the list of languages
21851 _mainFileFetcher.get('languages'), // load the list of supported locales
21852 _mainFileFetcher.get('locales')]).then(function (results) {
21853 _dataLanguages = results[0];
21854 _dataLocales = results[1];
21855 }).then(function () {
21856 var requestedLocales = (_preferredLocaleCodes || []). // List of locales preferred by the browser in priority order.
21857 concat(utilDetect().browserLocales) // fallback to English since it's the only guaranteed complete language
21860 _localeCodes = localesToUseFrom(requestedLocales); // Run iD in the highest-priority locale; the rest are fallbacks
21862 _localeCode = _localeCodes[0]; // Will always return the index for `en` if nothing else
21864 var fullCoverageIndex = _localeCodes.findIndex(function (locale) {
21865 return _dataLocales[locale].pct === 1;
21866 }); // We only need to load locales up until we find one with full coverage
21869 var loadStringsPromises = _localeCodes.slice(0, fullCoverageIndex + 1).map(function (code) {
21870 return localizer.loadLocale(code);
21873 return Promise.all(loadStringsPromises);
21874 }).then(function () {
21875 updateForCurrentLocale();
21876 })["catch"](function (err) {
21877 return console.error(err);
21878 }); // eslint-disable-line
21879 }; // Returns the locales from `requestedLocales` supported by iD that we should use
21882 function localesToUseFrom(requestedLocales) {
21883 var supportedLocales = _dataLocales;
21886 for (var i in requestedLocales) {
21887 var locale = requestedLocales[i];
21888 if (supportedLocales[locale]) toUse.push(locale);
21890 if (locale.includes('-')) {
21891 // Full locale ('es-ES'), add fallback to the base ('es')
21892 var langPart = locale.split('-')[0];
21893 if (supportedLocales[langPart]) toUse.push(langPart);
21895 } // remove duplicates
21898 return utilArrayUniq(toUse);
21901 function updateForCurrentLocale() {
21902 if (!_localeCode) return;
21903 _languageCode = _localeCode.split('-')[0];
21904 var currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
21905 var hash = utilStringQs(window.location.hash);
21907 if (hash.rtl === 'true') {
21908 _textDirection = 'rtl';
21909 } else if (hash.rtl === 'false') {
21910 _textDirection = 'ltr';
21912 _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
21915 var locale = _localeCode;
21916 if (locale.toLowerCase() === 'en-us') locale = 'en';
21917 _languageNames = _localeStrings[locale].languageNames;
21918 _scriptNames = _localeStrings[locale].scriptNames;
21919 _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
21922 // Returns a Promise to load the strings for the requested locale
21925 localizer.loadLocale = function (requested) {
21926 if (!_dataLocales) {
21927 return Promise.reject('loadLocale called before init');
21930 var locale = requested; // US English is the default
21932 if (locale.toLowerCase() === 'en-us') locale = 'en';
21934 if (!_dataLocales[locale]) {
21935 return Promise.reject("Unsupported locale: ".concat(requested));
21938 if (_localeStrings[locale]) {
21940 return Promise.resolve(locale);
21943 var fileMap = _mainFileFetcher.fileMap();
21944 var key = "locale_".concat(locale);
21945 fileMap[key] = "locales/".concat(locale, ".json");
21946 return _mainFileFetcher.get(key).then(function (d) {
21947 _localeStrings[locale] = d[locale];
21952 localizer.pluralRule = function (number) {
21953 return pluralRule(number, _localeCode);
21954 }; // Returns the plural rule for the given `number` with the given `localeCode`.
21955 // One of: `zero`, `one`, `two`, `few`, `many`, `other`
21958 function pluralRule(number, localeCode) {
21959 // modern browsers have this functionality built-in
21960 var rules = 'Intl' in window && Intl.PluralRules && new Intl.PluralRules(localeCode);
21963 return rules.select(number);
21964 } // fallback to basic one/other, as in English
21967 if (number === 1) return 'one';
21971 * Try to find that string in `locale` or the current `_localeCode` matching
21972 * the given `stringId`. If no string can be found in the requested locale,
21973 * we'll recurse down all the `_localeCodes` until one is found.
21975 * @param {string} stringId string identifier
21976 * @param {object?} replacements token replacements and default string
21977 * @param {string?} locale locale to use (defaults to currentLocale)
21978 * @return {string?} localized string
21982 localizer.tInfo = function (stringId, replacements, locale) {
21983 locale = locale || _localeCode;
21984 var path = stringId.split('.').map(function (s) {
21985 return s.replace(/<TX_DOT>/g, '.');
21987 var stringsKey = locale; // US English is the default
21989 if (stringsKey.toLowerCase() === 'en-us') stringsKey = 'en';
21990 var result = _localeStrings[stringsKey];
21992 while (result !== undefined && path.length) {
21993 result = result[path.pop()];
21996 if (result !== undefined) {
21997 if (replacements) {
21998 if (_typeof(result) === 'object' && Object.keys(result).length) {
21999 // If plural forms are provided, dig one level deeper based on the
22000 // first numeric token replacement provided.
22001 var number = Object.values(replacements).find(function (value) {
22002 return typeof value === 'number';
22005 if (number !== undefined) {
22006 var rule = pluralRule(number, locale);
22008 if (result[rule]) {
22009 result = result[rule];
22011 // We're pretty sure this should be a plural but no string
22012 // could be found for the given rule. Just pick the first
22013 // string and hope it makes sense.
22014 result = Object.values(result)[0];
22019 if (typeof result === 'string') {
22020 for (var key in replacements) {
22021 var value = replacements[key];
22023 if (typeof value === 'number' && value.toLocaleString) {
22024 // format numbers for the locale
22025 value = value.toLocaleString(locale, {
22028 minimumFractionDigits: 0
22032 var token = "{".concat(key, "}");
22033 var regex = new RegExp(token, 'g');
22034 result = result.replace(regex, value);
22039 if (typeof result === 'string') {
22040 // found a localized string!
22046 } // no localized string found...
22047 // attempt to fallback to a lower-priority language
22050 var index = _localeCodes.indexOf(locale);
22052 if (index >= 0 && index < _localeCodes.length - 1) {
22053 // eventually this will be 'en' or another locale with 100% coverage
22054 var fallback = _localeCodes[index + 1];
22055 return localizer.tInfo(stringId, replacements, fallback);
22058 if (replacements && 'default' in replacements) {
22059 // Fallback to a default value if one is specified in `replacements`
22061 text: replacements["default"],
22066 var missing = "Missing ".concat(locale, " translation: ").concat(stringId);
22067 if (typeof console !== 'undefined') console.error(missing); // eslint-disable-line
22073 }; // Returns only the localized text, discarding the locale info
22076 localizer.t = function (stringId, replacements, locale) {
22077 return localizer.tInfo(stringId, replacements, locale).text;
22078 }; // Returns the localized text wrapped in an HTML element encoding the locale info
22081 localizer.t.html = function (stringId, replacements, locale) {
22082 var info = localizer.tInfo(stringId, replacements, locale); // text may be empty or undefined if `replacements.default` is
22084 return info.text ? localizer.htmlForLocalizedText(info.text, info.locale) : '';
22087 localizer.htmlForLocalizedText = function (text, localeCode) {
22088 return "<span class=\"localized-text\" lang=\"".concat(localeCode || 'unknown', "\">").concat(text, "</span>");
22091 localizer.languageName = function (code, options) {
22092 if (_languageNames[code]) {
22093 // name in locale language
22095 return _languageNames[code];
22096 } // sometimes we only want the local name
22099 if (options && options.localOnly) return null;
22100 var langInfo = _dataLanguages[code];
22103 if (langInfo.nativeName) {
22104 // name in native language
22105 // e.g. "Deutsch (de)"
22106 return localizer.t('translate.language_and_code', {
22107 language: langInfo.nativeName,
22110 } else if (langInfo.base && langInfo.script) {
22111 var base = langInfo.base; // the code of the language this is based on
22113 if (_languageNames[base]) {
22114 // base language name in locale language
22115 var scriptCode = langInfo.script;
22116 var script = _scriptNames[scriptCode] || scriptCode; // e.g. "Serbian (Cyrillic)"
22118 return localizer.t('translate.language_and_code', {
22119 language: _languageNames[base],
22122 } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
22123 // e.g. "српски (sr-Cyrl)"
22124 return localizer.t('translate.language_and_code', {
22125 language: _dataLanguages[base].nativeName,
22132 return code; // if not found, use the code
22138 // `presetCollection` is a wrapper around an `Array` of presets `collection`,
22139 // and decorated with some extra methods for searching and matching geometry
22142 function presetCollection(collection) {
22143 var MAXRESULTS = 50;
22146 _this.collection = collection;
22148 _this.item = function (id) {
22149 if (_memo[id]) return _memo[id];
22151 var found = _this.collection.find(function (d) {
22152 return d.id === id;
22155 if (found) _memo[id] = found;
22159 _this.index = function (id) {
22160 return _this.collection.findIndex(function (d) {
22161 return d.id === id;
22165 _this.matchGeometry = function (geometry) {
22166 return presetCollection(_this.collection.filter(function (d) {
22167 return d.matchGeometry(geometry);
22171 _this.matchAllGeometry = function (geometries) {
22172 return presetCollection(_this.collection.filter(function (d) {
22173 return d && d.matchAllGeometry(geometries);
22177 _this.matchAnyGeometry = function (geometries) {
22178 return presetCollection(_this.collection.filter(function (d) {
22179 return geometries.some(function (geom) {
22180 return d.matchGeometry(geom);
22185 _this.fallback = function (geometry) {
22187 if (id === 'vertex') id = 'point';
22188 return _this.item(id);
22191 _this.search = function (value, geometry, countryCode) {
22192 if (!value) return _this;
22193 value = value.toLowerCase().trim(); // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
22195 function leading(a) {
22196 var index = a.indexOf(value);
22197 return index === 0 || a[index - 1] === ' ';
22198 } // match at name beginning only
22201 function leadingStrict(a) {
22202 var index = a.indexOf(value);
22203 return index === 0;
22206 function sortNames(a, b) {
22207 var aCompare = (a.suggestion ? a.originalName : a.name()).toLowerCase();
22208 var bCompare = (b.suggestion ? b.originalName : b.name()).toLowerCase(); // priority if search string matches preset name exactly - #4325
22210 if (value === aCompare) return -1;
22211 if (value === bCompare) return 1; // priority for higher matchScore
22213 var i = b.originalScore - a.originalScore;
22214 if (i !== 0) return i; // priority if search string appears earlier in preset name
22216 i = aCompare.indexOf(value) - bCompare.indexOf(value);
22217 if (i !== 0) return i; // priority for shorter preset names
22219 return aCompare.length - bCompare.length;
22222 var pool = _this.collection;
22225 pool = pool.filter(function (a) {
22226 if (a.countryCodes && a.countryCodes.indexOf(countryCode) === -1) return false;
22227 if (a.notCountryCodes && a.notCountryCodes.indexOf(countryCode) !== -1) return false;
22232 var searchable = pool.filter(function (a) {
22233 return a.searchable !== false && a.suggestion !== true;
22235 var suggestions = pool.filter(function (a) {
22236 return a.suggestion === true;
22237 }); // matches value to preset.name
22239 var leading_name = searchable.filter(function (a) {
22240 return leading(a.name().toLowerCase());
22241 }).sort(sortNames); // matches value to preset suggestion name (original name is unhyphenated)
22243 var leading_suggestions = suggestions.filter(function (a) {
22244 return leadingStrict(a.originalName.toLowerCase());
22245 }).sort(sortNames); // matches value to preset.terms values
22247 var leading_terms = searchable.filter(function (a) {
22248 return (a.terms() || []).some(leading);
22249 }); // matches value to preset.tags values
22251 var leading_tag_values = searchable.filter(function (a) {
22252 return Object.values(a.tags || {}).filter(function (val) {
22253 return val !== '*';
22255 }); // finds close matches to value in preset.name
22257 var similar_name = searchable.map(function (a) {
22260 dist: utilEditDistance(value, a.name())
22262 }).filter(function (a) {
22263 return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
22264 }).sort(function (a, b) {
22265 return a.dist - b.dist;
22266 }).map(function (a) {
22268 }); // finds close matches to value to preset suggestion name (original name is unhyphenated)
22270 var similar_suggestions = suggestions.map(function (a) {
22273 dist: utilEditDistance(value, a.originalName.toLowerCase())
22275 }).filter(function (a) {
22276 return a.dist + Math.min(value.length - a.preset.originalName.length, 0) < 1;
22277 }).sort(function (a, b) {
22278 return a.dist - b.dist;
22279 }).map(function (a) {
22281 }); // finds close matches to value in preset.terms
22283 var similar_terms = searchable.filter(function (a) {
22284 return (a.terms() || []).some(function (b) {
22285 return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
22288 var results = leading_name.concat(leading_suggestions, leading_terms, leading_tag_values, similar_name, similar_suggestions, similar_terms).slice(0, MAXRESULTS - 1);
22291 if (typeof geometry === 'string') {
22292 results.push(_this.fallback(geometry));
22294 geometry.forEach(function (geom) {
22295 return results.push(_this.fallback(geom));
22300 return presetCollection(utilArrayUniq(results));
22306 // `presetCategory` builds a `presetCollection` of member presets,
22307 // decorated with some extra methods for searching and matching geometry
22310 function presetCategory(categoryID, category, all) {
22311 var _this = Object.assign({}, category); // shallow copy
22314 _this.id = categoryID;
22315 _this.members = presetCollection(category.members.map(function (presetID) {
22316 return all.item(presetID);
22317 }).filter(Boolean));
22318 _this.geometry = _this.members.collection.reduce(function (acc, preset) {
22319 for (var i in preset.geometry) {
22320 var geometry = preset.geometry[i];
22322 if (acc.indexOf(geometry) === -1) {
22323 acc.push(geometry);
22330 _this.matchGeometry = function (geom) {
22331 return _this.geometry.indexOf(geom) >= 0;
22334 _this.matchAllGeometry = function (geometries) {
22335 return _this.members.collection.some(function (preset) {
22336 return preset.matchAllGeometry(geometries);
22340 _this.matchScore = function () {
22344 _this.name = function () {
22345 return _t("presets.categories.".concat(categoryID, ".name"), {
22346 'default': categoryID
22350 _this.nameLabel = function () {
22351 return _t.html("presets.categories.".concat(categoryID, ".name"), {
22352 'default': categoryID
22356 _this.terms = function () {
22363 // `presetField` decorates a given `field` Object
22364 // with some extra methods for searching and matching geometry
22367 function presetField(fieldID, field) {
22368 var _this = Object.assign({}, field); // shallow copy
22371 _this.id = fieldID; // for use in classes, element ids, css selectors
22373 _this.safeid = utilSafeClassName(fieldID);
22375 _this.matchGeometry = function (geom) {
22376 return !_this.geometry || _this.geometry.indexOf(geom) !== -1;
22379 _this.matchAllGeometry = function (geometries) {
22380 return !_this.geometry || geometries.every(function (geom) {
22381 return _this.geometry.indexOf(geom) !== -1;
22385 _this.t = function (scope, options) {
22386 return _t("presets.fields.".concat(fieldID, ".").concat(scope), options);
22389 _this.t.html = function (scope, options) {
22390 return _t.html("presets.fields.".concat(fieldID, ".").concat(scope), options);
22393 _this.title = function () {
22394 return _this.overrideLabel || _this.t('label', {
22399 _this.label = function () {
22400 return _this.overrideLabel || _this.t.html('label', {
22405 var _placeholder = _this.placeholder;
22407 _this.placeholder = function () {
22408 return _this.t('placeholder', {
22409 'default': _placeholder
22413 _this.originalTerms = (_this.terms || []).join();
22415 _this.terms = function () {
22416 return _this.t('terms', {
22417 'default': _this.originalTerms
22418 }).toLowerCase().trim().split(/\s*,+\s*/);
22421 _this.increment = _this.type === 'number' ? _this.increment || 1 : undefined;
22425 // `Array.prototype.lastIndexOf` method
22426 // https://tc39.github.io/ecma262/#sec-array.prototype.lastindexof
22427 _export({ target: 'Array', proto: true, forced: arrayLastIndexOf !== [].lastIndexOf }, {
22428 lastIndexOf: arrayLastIndexOf
22431 // `presetPreset` decorates a given `preset` Object
22432 // with some extra methods for searching and matching geometry
22435 function presetPreset(presetID, preset, addable, allFields, allPresets) {
22436 allFields = allFields || {};
22437 allPresets = allPresets || {};
22439 var _this = Object.assign({}, preset); // shallow copy
22442 var _addable = addable || false;
22444 var _resolvedFields; // cache
22447 var _resolvedMoreFields; // cache
22450 _this.id = presetID;
22451 _this.safeid = utilSafeClassName(presetID); // for use in css classes, selectors, element ids
22453 _this.originalTerms = (_this.terms || []).join();
22454 _this.originalName = _this.name || '';
22455 _this.originalScore = _this.matchScore || 1;
22456 _this.originalReference = _this.reference || {};
22457 _this.originalFields = _this.fields || [];
22458 _this.originalMoreFields = _this.moreFields || [];
22460 _this.fields = function () {
22461 return _resolvedFields || (_resolvedFields = resolve('fields'));
22464 _this.moreFields = function () {
22465 return _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields'));
22468 _this.resetFields = function () {
22469 return _resolvedFields = _resolvedMoreFields = null;
22472 _this.tags = _this.tags || {};
22473 _this.addTags = _this.addTags || _this.tags;
22474 _this.removeTags = _this.removeTags || _this.addTags;
22475 _this.geometry = _this.geometry || [];
22477 _this.matchGeometry = function (geom) {
22478 return _this.geometry.indexOf(geom) >= 0;
22481 _this.matchAllGeometry = function (geoms) {
22482 return geoms.every(_this.matchGeometry);
22485 _this.matchScore = function (entityTags) {
22486 var tags = _this.tags;
22488 var score = 0; // match on tags
22490 for (var k in tags) {
22493 if (entityTags[k] === tags[k]) {
22494 score += _this.originalScore;
22495 } else if (tags[k] === '*' && k in entityTags) {
22496 score += _this.originalScore / 2;
22500 } // boost score for additional matches in addTags - #6802
22503 var addTags = _this.addTags;
22505 for (var _k in addTags) {
22506 if (!seen[_k] && entityTags[_k] === addTags[_k]) {
22507 score += _this.originalScore;
22514 _this.t = function (scope, options) {
22515 var textID = "presets.presets.".concat(presetID, ".").concat(scope);
22516 return _t(textID, options);
22519 _this.t.html = function (scope, options) {
22520 var textID = "presets.presets.".concat(presetID, ".").concat(scope);
22521 return _t.html(textID, options);
22524 _this.name = function () {
22525 return _this.t('name', {
22526 'default': _this.originalName
22530 _this.nameLabel = function () {
22531 return _this.t.html('name', {
22532 'default': _this.originalName
22536 _this.subtitle = function () {
22537 if (_this.suggestion) {
22538 var path = presetID.split('/');
22539 path.pop(); // remove brand name
22541 return _t('presets.presets.' + path.join('/') + '.name');
22547 _this.subtitleLabel = function () {
22548 if (_this.suggestion) {
22549 var path = presetID.split('/');
22550 path.pop(); // remove brand name
22552 return _t.html('presets.presets.' + path.join('/') + '.name');
22558 _this.terms = function () {
22559 return _this.t('terms', {
22560 'default': _this.originalTerms
22561 }).toLowerCase().trim().split(/\s*,+\s*/);
22564 _this.isFallback = function () {
22565 var tagCount = Object.keys(_this.tags).length;
22566 return tagCount === 0 || tagCount === 1 && _this.tags.hasOwnProperty('area');
22569 _this.addable = function (val) {
22570 if (!arguments.length) return _addable;
22575 _this.reference = function () {
22576 // Lookup documentation on Wikidata...
22577 var qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata'];
22583 } // Lookup documentation on OSM Wikibase...
22586 var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
22587 var value = _this.originalReference.value || _this.tags[key];
22589 if (value === '*') {
22601 _this.unsetTags = function (tags, geometry, skipFieldDefaults) {
22602 tags = utilObjectOmit(tags, Object.keys(_this.removeTags));
22604 if (geometry && !skipFieldDefaults) {
22605 _this.fields().forEach(function (field) {
22606 if (field.matchGeometry(geometry) && field.key && field["default"] === tags[field.key]) {
22607 delete tags[field.key];
22616 _this.setTags = function (tags, geometry, skipFieldDefaults) {
22617 var addTags = _this.addTags;
22618 tags = Object.assign({}, tags); // shallow copy
22620 for (var k in addTags) {
22621 if (addTags[k] === '*') {
22624 tags[k] = addTags[k];
22626 } // Add area=yes if necessary.
22627 // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
22628 // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
22629 // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
22632 if (!addTags.hasOwnProperty('area')) {
22635 if (geometry === 'area') {
22636 var needsAreaTag = true;
22638 if (_this.geometry.indexOf('line') === -1) {
22639 for (var _k2 in addTags) {
22640 if (_k2 in osmAreaKeys) {
22641 needsAreaTag = false;
22647 if (needsAreaTag) {
22653 if (geometry && !skipFieldDefaults) {
22654 _this.fields().forEach(function (field) {
22655 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field["default"]) {
22656 tags[field.key] = field["default"];
22662 }; // For a preset without fields, use the fields of the parent preset.
22663 // Replace {preset} placeholders with the fields of the specified presets.
22666 function resolve(which) {
22667 var fieldIDs = which === 'fields' ? _this.originalFields : _this.originalMoreFields;
22669 fieldIDs.forEach(function (fieldID) {
22670 var match = fieldID.match(/\{(.*)\}/);
22672 if (match !== null) {
22673 // a presetID wrapped in braces {}
22674 resolved = resolved.concat(inheritFields(match[1], which));
22675 } else if (allFields[fieldID]) {
22676 // a normal fieldID
22677 resolved.push(allFields[fieldID]);
22679 console.log("Cannot resolve \"".concat(fieldID, "\" found in ").concat(_this.id, ".").concat(which)); // eslint-disable-line no-console
22681 }); // no fields resolved, so use the parent's if possible
22683 if (!resolved.length) {
22684 var endIndex = _this.id.lastIndexOf('/');
22686 var parentID = endIndex && _this.id.substring(0, endIndex);
22689 resolved = inheritFields(parentID, which);
22693 return utilArrayUniq(resolved); // returns an array of fields to inherit from the given presetID, if found
22695 function inheritFields(presetID, which) {
22696 var parent = allPresets[presetID];
22697 if (!parent) return [];
22699 if (which === 'fields') {
22700 return parent.fields().filter(shouldInherit);
22701 } else if (which === 'moreFields') {
22702 return parent.moreFields();
22706 } // Skip `fields` for the keys which define the preset.
22707 // These are usually `typeCombo` fields like `shop=*`
22710 function shouldInherit(f) {
22711 if (f.key && _this.tags[f.key] !== undefined && // inherit anyway if multiple values are allowed or just a checkbox
22712 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'manyCombo' && f.type !== 'check') return false;
22720 var _mainPresetIndex = presetIndex(); // singleton
22721 // `presetIndex` wraps a `presetCollection`
22722 // with methods for loading new data and returning defaults
22725 function presetIndex() {
22726 var dispatch$1 = dispatch('favoritePreset', 'recentsChange');
22727 var MAXRECENTS = 30; // seed the preset lists with geometry fallbacks
22729 var POINT = presetPreset('point', {
22732 geometry: ['point', 'vertex'],
22735 var LINE = presetPreset('line', {
22738 geometry: ['line'],
22741 var AREA = presetPreset('area', {
22746 geometry: ['area'],
22749 var RELATION = presetPreset('relation', {
22752 geometry: ['relation'],
22756 var _this = presetCollection([POINT, LINE, AREA, RELATION]);
22765 point: presetCollection([POINT]),
22766 vertex: presetCollection([POINT]),
22767 line: presetCollection([LINE]),
22768 area: presetCollection([AREA]),
22769 relation: presetCollection([RELATION])
22772 var _categories = {};
22773 var _universal = [];
22774 var _addablePresetIDs = null; // Set of preset IDs that the user can add
22778 var _favorites; // Index of presets by (geometry, tag key).
22781 var _geometryIndex = {
22791 _this.ensureLoaded = function () {
22792 if (_loadPromise) return _loadPromise;
22793 return _loadPromise = Promise.all([_mainFileFetcher.get('preset_categories'), _mainFileFetcher.get('preset_defaults'), _mainFileFetcher.get('preset_presets'), _mainFileFetcher.get('preset_fields')]).then(function (vals) {
22795 categories: vals[0],
22801 osmSetAreaKeys(_this.areaKeys());
22802 osmSetPointTags(_this.pointTags());
22803 osmSetVertexTags(_this.vertexTags());
22807 _this.merge = function (d) {
22810 Object.keys(d.fields).forEach(function (fieldID) {
22811 var f = d.fields[fieldID];
22815 _fields[fieldID] = presetField(fieldID, f);
22818 delete _fields[fieldID];
22825 Object.keys(d.presets).forEach(function (presetID) {
22826 var p = d.presets[presetID];
22830 var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
22832 _presets[presetID] = presetPreset(presetID, p, isAddable, _fields, _presets);
22834 // remove (but not if it's a fallback)
22835 var existing = _presets[presetID];
22837 if (existing && !existing.isFallback()) {
22838 delete _presets[presetID];
22842 } // Need to rebuild _this.collection before loading categories
22845 _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Categories
22847 if (d.categories) {
22848 Object.keys(d.categories).forEach(function (categoryID) {
22849 var c = d.categories[categoryID];
22853 _categories[categoryID] = presetCategory(categoryID, c, _this);
22856 delete _categories[categoryID];
22859 } // Rebuild _this.collection after loading categories
22862 _this.collection = Object.values(_presets).concat(Object.values(_categories)); // Merge Defaults
22865 Object.keys(d.defaults).forEach(function (geometry) {
22866 var def = d.defaults[geometry];
22868 if (Array.isArray(def)) {
22870 _defaults[geometry] = presetCollection(def.map(function (id) {
22871 return _presets[id] || _categories[id];
22872 }).filter(Boolean));
22875 delete _defaults[geometry];
22878 } // Rebuild universal fields array
22881 _universal = Object.values(_fields).filter(function (field) {
22882 return field.universal;
22883 }); // Reset all the preset fields - they'll need to be resolved again
22885 Object.values(_presets).forEach(function (preset) {
22886 return preset.resetFields();
22887 }); // Rebuild geometry index
22897 _this.collection.forEach(function (preset) {
22898 (preset.geometry || []).forEach(function (geometry) {
22899 var g = _geometryIndex[geometry];
22901 for (var key in preset.tags) {
22902 (g[key] = g[key] || []).push(preset);
22910 _this.match = function (entity, resolver) {
22911 return resolver["transient"](entity, 'presetMatch', function () {
22912 var geometry = entity.geometry(resolver); // Treat entities on addr:interpolation lines as points, not vertices - #3241
22914 if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
22915 geometry = 'point';
22918 return _this.matchTags(entity.tags, geometry);
22922 _this.matchTags = function (tags, geometry) {
22923 var geometryMatches = _geometryIndex[geometry];
22928 for (var k in tags) {
22929 // If any part of an address is present, allow fallback to "Address" preset - #4353
22930 if (/^addr:/.test(k) && geometryMatches['addr:*']) {
22931 address = geometryMatches['addr:*'][0];
22934 var keyMatches = geometryMatches[k];
22935 if (!keyMatches) continue;
22937 for (var i = 0; i < keyMatches.length; i++) {
22938 var score = keyMatches[i].matchScore(tags);
22940 if (score > best) {
22942 match = keyMatches[i];
22947 if (address && (!match || match.isFallback())) {
22951 return match || _this.fallback(geometry);
22954 _this.allowsVertex = function (entity, resolver) {
22955 if (entity.type !== 'node') return false;
22956 if (Object.keys(entity.tags).length === 0) return true;
22957 return resolver["transient"](entity, 'vertexMatch', function () {
22958 // address lines allow vertices to act as standalone points
22959 if (entity.isOnAddressLine(resolver)) return true;
22960 var geometries = osmNodeGeometriesForTags(entity.tags);
22961 if (geometries.vertex) return true;
22962 if (geometries.point) return false; // allow vertices for unspecified points
22966 }; // Because of the open nature of tagging, iD will never have a complete
22967 // list of tags used in OSM, so we want it to have logic like "assume
22968 // that a closed way with an amenity tag is an area, unless the amenity
22969 // is one of these specific types". This function computes a structure
22970 // that allows testing of such conditions, based on the presets designated
22971 // as as supporting (or not supporting) the area geometry.
22973 // The returned object L is a keeplist/discardlist of tags. A closed way
22974 // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
22975 // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
22976 // and the subkeys form the discardlist.
22979 _this.areaKeys = function () {
22980 // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
22981 var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
22982 var areaKeys = {}; // ignore name-suggestion-index and deprecated presets
22984 var presets = _this.collection.filter(function (p) {
22985 return !p.suggestion && !p.replacement;
22989 presets.forEach(function (p) {
22990 var keys = p.tags && Object.keys(p.tags);
22991 var key = keys && keys.length && keys[0]; // pick the first tag
22994 if (ignore.indexOf(key) !== -1) return;
22996 if (p.geometry.indexOf('area') !== -1) {
22997 // probably an area..
22998 areaKeys[key] = areaKeys[key] || {};
23002 presets.forEach(function (p) {
23005 for (key in p.addTags) {
23006 // examine all addTags to get a better sense of what can be tagged on lines - #6800
23007 var value = p.addTags[key];
23009 if (key in areaKeys && // probably an area...
23010 p.geometry.indexOf('line') !== -1 && // but sometimes a line
23012 areaKeys[key][value] = true;
23019 _this.pointTags = function () {
23020 return _this.collection.reduce(function (pointTags, d) {
23021 // ignore name-suggestion-index, deprecated, and generic presets
23022 if (d.suggestion || d.replacement || d.searchable === false) return pointTags; // only care about the primary tag
23024 var keys = d.tags && Object.keys(d.tags);
23025 var key = keys && keys.length && keys[0]; // pick the first tag
23027 if (!key) return pointTags; // if this can be a point
23029 if (d.geometry.indexOf('point') !== -1) {
23030 pointTags[key] = pointTags[key] || {};
23031 pointTags[key][d.tags[key]] = true;
23038 _this.vertexTags = function () {
23039 return _this.collection.reduce(function (vertexTags, d) {
23040 // ignore name-suggestion-index, deprecated, and generic presets
23041 if (d.suggestion || d.replacement || d.searchable === false) return vertexTags; // only care about the primary tag
23043 var keys = d.tags && Object.keys(d.tags);
23044 var key = keys && keys.length && keys[0]; // pick the first tag
23046 if (!key) return vertexTags; // if this can be a vertex
23048 if (d.geometry.indexOf('vertex') !== -1) {
23049 vertexTags[key] = vertexTags[key] || {};
23050 vertexTags[key][d.tags[key]] = true;
23057 _this.field = function (id) {
23058 return _fields[id];
23061 _this.universal = function () {
23065 _this.defaults = function (geometry, n, startWithRecents) {
23068 if (startWithRecents) {
23069 recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
23074 if (_addablePresetIDs) {
23075 defaults = Array.from(_addablePresetIDs).map(function (id) {
23076 var preset = _this.item(id);
23078 if (preset && preset.matchGeometry(geometry)) return preset;
23080 }).filter(Boolean);
23082 defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
23085 return presetCollection(utilArrayUniq(recents.concat(defaults)).slice(0, n - 1));
23086 }; // pass a Set of addable preset ids
23089 _this.addablePresetIDs = function (val) {
23090 if (!arguments.length) return _addablePresetIDs; // accept and convert arrays
23092 if (Array.isArray(val)) val = new Set(val);
23093 _addablePresetIDs = val;
23095 if (_addablePresetIDs) {
23096 // reset all presets
23097 _this.collection.forEach(function (p) {
23098 // categories aren't addable
23099 if (p.addable) p.addable(_addablePresetIDs.has(p.id));
23102 _this.collection.forEach(function (p) {
23103 if (p.addable) p.addable(true);
23110 _this.recent = function () {
23111 return presetCollection(utilArrayUniq(_this.getRecents().map(function (d) {
23116 function RibbonItem(preset, source) {
23118 item.preset = preset;
23119 item.source = source;
23121 item.isFavorite = function () {
23122 return item.source === 'favorite';
23125 item.isRecent = function () {
23126 return item.source === 'recent';
23129 item.matches = function (preset) {
23130 return item.preset.id === preset.id;
23133 item.minified = function () {
23135 pID: item.preset.id
23142 function ribbonItemForMinified(d, source) {
23144 var preset = _this.item(d.pID);
23146 if (!preset) return null;
23147 return RibbonItem(preset, source);
23153 _this.getGenericRibbonItems = function () {
23154 return ['point', 'line', 'area'].map(function (id) {
23155 return RibbonItem(_this.item(id), 'generic');
23159 _this.getAddable = function () {
23160 if (!_addablePresetIDs) return [];
23161 return _addablePresetIDs.map(function (id) {
23162 var preset = _this.item(id);
23164 if (preset) return RibbonItem(preset, 'addable');
23166 }).filter(Boolean);
23169 function setRecents(items) {
23171 var minifiedItems = items.map(function (d) {
23172 return d.minified();
23174 corePreferences('preset_recents', JSON.stringify(minifiedItems));
23175 dispatch$1.call('recentsChange');
23178 _this.getRecents = function () {
23180 // fetch from local storage
23181 _recents = (JSON.parse(corePreferences('preset_recents')) || []).reduce(function (acc, d) {
23182 var item = ribbonItemForMinified(d, 'recent');
23183 if (item && item.preset.addable()) acc.push(item);
23191 _this.addRecent = function (preset, besidePreset, after) {
23192 var recents = _this.getRecents();
23194 var beforeItem = _this.recentMatching(besidePreset);
23196 var toIndex = recents.indexOf(beforeItem);
23197 if (after) toIndex += 1;
23198 var newItem = RibbonItem(preset, 'recent');
23199 recents.splice(toIndex, 0, newItem);
23200 setRecents(recents);
23203 _this.removeRecent = function (preset) {
23204 var item = _this.recentMatching(preset);
23207 var items = _this.getRecents();
23209 items.splice(items.indexOf(item), 1);
23214 _this.recentMatching = function (preset) {
23215 var items = _this.getRecents();
23217 for (var i in items) {
23218 if (items[i].matches(preset)) {
23226 _this.moveItem = function (items, fromIndex, toIndex) {
23227 if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= items.length || toIndex >= items.length) return null;
23228 items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
23232 _this.moveRecent = function (item, beforeItem) {
23233 var recents = _this.getRecents();
23235 var fromIndex = recents.indexOf(item);
23236 var toIndex = recents.indexOf(beforeItem);
23238 var items = _this.moveItem(recents, fromIndex, toIndex);
23240 if (items) setRecents(items);
23243 _this.setMostRecent = function (preset) {
23244 if (preset.searchable === false) return;
23246 var items = _this.getRecents();
23248 var item = _this.recentMatching(preset);
23251 items.splice(items.indexOf(item), 1);
23253 item = RibbonItem(preset, 'recent');
23254 } // remove the last recent (first in, first out)
23257 while (items.length >= MAXRECENTS) {
23262 items.unshift(item);
23266 function setFavorites(items) {
23267 _favorites = items;
23268 var minifiedItems = items.map(function (d) {
23269 return d.minified();
23271 corePreferences('preset_favorites', JSON.stringify(minifiedItems)); // call update
23273 dispatch$1.call('favoritePreset');
23276 _this.addFavorite = function (preset, besidePreset, after) {
23277 var favorites = _this.getFavorites();
23279 var beforeItem = _this.favoriteMatching(besidePreset);
23281 var toIndex = favorites.indexOf(beforeItem);
23282 if (after) toIndex += 1;
23283 var newItem = RibbonItem(preset, 'favorite');
23284 favorites.splice(toIndex, 0, newItem);
23285 setFavorites(favorites);
23288 _this.toggleFavorite = function (preset) {
23289 var favs = _this.getFavorites();
23291 var favorite = _this.favoriteMatching(preset);
23294 favs.splice(favs.indexOf(favorite), 1);
23296 // only allow 10 favorites
23297 if (favs.length === 10) {
23298 // remove the last favorite (last in, first out)
23303 favs.push(RibbonItem(preset, 'favorite'));
23306 setFavorites(favs);
23309 _this.removeFavorite = function (preset) {
23310 var item = _this.favoriteMatching(preset);
23313 var items = _this.getFavorites();
23315 items.splice(items.indexOf(item), 1);
23316 setFavorites(items);
23320 _this.getFavorites = function () {
23322 // fetch from local storage
23323 var rawFavorites = JSON.parse(corePreferences('preset_favorites'));
23325 if (!rawFavorites) {
23327 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
23330 _favorites = rawFavorites.reduce(function (output, d) {
23331 var item = ribbonItemForMinified(d, 'favorite');
23332 if (item && item.preset.addable()) output.push(item);
23340 _this.favoriteMatching = function (preset) {
23341 var favs = _this.getFavorites();
23343 for (var index in favs) {
23344 if (favs[index].matches(preset)) {
23345 return favs[index];
23352 return utilRebind(_this, dispatch$1, 'on');
23355 function utilTagText(entity) {
23356 var obj = entity && entity.tags || {};
23357 return Object.keys(obj).map(function (k) {
23358 return k + '=' + obj[k];
23361 function utilTotalExtent(array, graph) {
23362 var extent = geoExtent();
23365 for (var i = 0; i < array.length; i++) {
23367 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
23370 extent._extend(entity.extent(graph));
23376 function utilTagDiff(oldTags, newTags) {
23378 var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
23379 keys.forEach(function (k) {
23380 var oldVal = oldTags[k];
23381 var newVal = newTags[k];
23383 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
23389 display: '- ' + k + '=' + oldVal
23393 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
23399 display: '+ ' + k + '=' + newVal
23405 function utilEntitySelector(ids) {
23406 return ids.length ? '.' + ids.join(',.') : 'nothing';
23407 } // returns an selector to select entity ids for:
23408 // - entityIDs passed in
23409 // - shallow descendant entityIDs for any of those entities that are relations
23411 function utilEntityOrMemberSelector(ids, graph) {
23412 var seen = new Set(ids);
23413 ids.forEach(collectShallowDescendants);
23414 return utilEntitySelector(Array.from(seen));
23416 function collectShallowDescendants(id) {
23417 var entity = graph.hasEntity(id);
23418 if (!entity || entity.type !== 'relation') return;
23419 entity.members.map(function (member) {
23421 }).forEach(function (id) {
23425 } // returns an selector to select entity ids for:
23426 // - entityIDs passed in
23427 // - deep descendant entityIDs for any of those entities that are relations
23429 function utilEntityOrDeepMemberSelector(ids, graph) {
23430 return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
23431 } // returns an selector to select entity ids for:
23432 // - entityIDs passed in
23433 // - deep descendant entityIDs for any of those entities that are relations
23435 function utilEntityAndDeepMemberIDs(ids, graph) {
23436 var seen = new Set();
23437 ids.forEach(collectDeepDescendants);
23438 return Array.from(seen);
23440 function collectDeepDescendants(id) {
23441 if (seen.has(id)) return;
23443 var entity = graph.hasEntity(id);
23444 if (!entity || entity.type !== 'relation') return;
23445 entity.members.map(function (member) {
23447 }).forEach(collectDeepDescendants); // recurse
23449 } // returns an selector to select entity ids for:
23450 // - deep descendant entityIDs for any of those entities that are relations
23452 function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
23453 var idsSet = new Set(ids);
23454 var seen = new Set();
23455 var returners = new Set();
23456 ids.forEach(collectDeepDescendants);
23457 return utilEntitySelector(Array.from(returners));
23459 function collectDeepDescendants(id) {
23460 if (seen.has(id)) return;
23463 if (!idsSet.has(id)) {
23467 var entity = graph.hasEntity(id);
23468 if (!entity || entity.type !== 'relation') return;
23469 if (skipMultipolgonMembers && entity.isMultipolygon()) return;
23470 entity.members.map(function (member) {
23472 }).forEach(collectDeepDescendants); // recurse
23474 } // Adds or removes highlight styling for the specified entities
23476 function utilHighlightEntities(ids, highlighted, context) {
23477 context.surface().selectAll(utilEntityOrDeepMemberSelector(ids, context.graph())).classed('highlighted', highlighted);
23478 } // returns an Array that is the union of:
23479 // - nodes for any nodeIDs passed in
23480 // - child nodes of any wayIDs passed in
23481 // - descendant member and child nodes of relationIDs passed in
23483 function utilGetAllNodes(ids, graph) {
23484 var seen = new Set();
23485 var nodes = new Set();
23486 ids.forEach(collectNodes);
23487 return Array.from(nodes);
23489 function collectNodes(id) {
23490 if (seen.has(id)) return;
23492 var entity = graph.hasEntity(id);
23493 if (!entity) return;
23495 if (entity.type === 'node') {
23497 } else if (entity.type === 'way') {
23498 entity.nodes.forEach(collectNodes);
23500 entity.members.map(function (member) {
23502 }).forEach(collectNodes); // recurse
23506 function utilDisplayName(entity) {
23507 var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
23508 var name = entity.tags[localizedNameKey] || entity.tags.name || '';
23509 var network = entity.tags.cycle_network || entity.tags.network;
23511 if (!name && entity.tags.ref) {
23512 name = entity.tags.ref;
23515 name = network + ' ' + name;
23521 function utilDisplayNameForPath(entity) {
23522 var name = utilDisplayName(entity);
23523 var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
23525 if (!isFirefox && name && rtlRegex.test(name)) {
23526 name = fixRTLTextForSvg(name);
23531 function utilDisplayType(id) {
23533 n: _t('inspector.node'),
23534 w: _t('inspector.way'),
23535 r: _t('inspector.relation')
23538 function utilDisplayLabel(entity, graphOrGeometry) {
23539 var displayName = utilDisplayName(entity);
23542 // use the display name if there is one
23543 return displayName;
23546 var preset = typeof graphOrGeometry === 'string' ? _mainPresetIndex.matchTags(entity.tags, graphOrGeometry) : _mainPresetIndex.match(entity, graphOrGeometry);
23548 if (preset && preset.name()) {
23549 // use the preset name if there is a match
23550 return preset.name();
23551 } // fallback to the display type (node/way/relation)
23554 return utilDisplayType(entity.id);
23556 function utilEntityRoot(entityType) {
23562 } // Returns a single object containing the tags of all the given entities.
23565 // highway: 'service',
23566 // service: 'parking_aisle'
23570 // highway: 'service',
23571 // service: 'driveway',
23576 // highway: 'service',
23577 // service: [ 'driveway', 'parking_aisle' ],
23578 // width: [ '3', undefined ]
23581 function utilCombinedTags(entityIDs, graph) {
23583 var tagCounts = {};
23584 var allKeys = new Set();
23585 var entities = entityIDs.map(function (entityID) {
23586 return graph.hasEntity(entityID);
23587 }).filter(Boolean); // gather the aggregate keys
23589 entities.forEach(function (entity) {
23590 var keys = Object.keys(entity.tags).filter(Boolean);
23591 keys.forEach(function (key) {
23595 entities.forEach(function (entity) {
23596 allKeys.forEach(function (key) {
23597 var value = entity.tags[key]; // purposely allow `undefined`
23599 if (!tags.hasOwnProperty(key)) {
23600 // first value, set as raw
23603 if (!Array.isArray(tags[key])) {
23604 if (tags[key] !== value) {
23605 // first alternate value, replace single value with array
23606 tags[key] = [tags[key], value];
23610 if (tags[key].indexOf(value) === -1) {
23611 // subsequent alternate value, add to array
23612 tags[key].push(value);
23617 var tagHash = key + '=' + value;
23618 if (!tagCounts[tagHash]) tagCounts[tagHash] = 0;
23619 tagCounts[tagHash] += 1;
23623 for (var key in tags) {
23624 if (!Array.isArray(tags[key])) continue; // sort values by frequency then alphabetically
23626 tags[key] = tags[key].sort(function (val1, val2) {
23627 var key = key; // capture
23629 var count2 = tagCounts[key + '=' + val2];
23630 var count1 = tagCounts[key + '=' + val1];
23632 if (count2 !== count1) {
23633 return count2 - count1;
23636 if (val2 && val1) {
23637 return val1.localeCompare(val2);
23640 return val1 ? 1 : -1;
23646 function utilStringQs(str) {
23647 var i = 0; // advance past any leading '?' or '#' characters
23649 while (i < str.length && (str[i] === '?' || str[i] === '#')) {
23653 str = str.slice(i);
23654 return str.split('&').reduce(function (obj, pair) {
23655 var parts = pair.split('=');
23657 if (parts.length === 2) {
23658 obj[parts[0]] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
23664 function utilQsString(obj, noencode) {
23665 // encode everything except special characters used in certain hash parameters:
23666 // "/" in map states, ":", ",", {" and "}" in background
23667 function softEncode(s) {
23668 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
23671 return Object.keys(obj).sort().map(function (key) {
23672 return encodeURIComponent(key) + '=' + (noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
23675 function utilPrefixDOMProperty(property) {
23676 var prefixes = ['webkit', 'ms', 'moz', 'o'];
23678 var n = prefixes.length;
23679 var s = document.body;
23680 if (property in s) return property;
23681 property = property.substr(0, 1).toUpperCase() + property.substr(1);
23684 if (prefixes[i] + property in s) {
23685 return prefixes[i] + property;
23691 function utilPrefixCSSProperty(property) {
23692 var prefixes = ['webkit', 'ms', 'Moz', 'O'];
23694 var n = prefixes.length;
23695 var s = document.body.style;
23697 if (property.toLowerCase() in s) {
23698 return property.toLowerCase();
23702 if (prefixes[i] + property in s) {
23703 return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
23709 var transformProperty;
23710 function utilSetTransform(el, x, y, scale) {
23711 var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
23712 var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' : 'translate3d(' + x + 'px,' + y + 'px,0)';
23713 return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
23714 } // Calculates Levenshtein distance between two strings
23715 // see: https://en.wikipedia.org/wiki/Levenshtein_distance
23716 // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
23718 function utilEditDistance(a, b) {
23719 a = remove$1(a.toLowerCase());
23720 b = remove$1(b.toLowerCase());
23721 if (a.length === 0) return b.length;
23722 if (b.length === 0) return a.length;
23726 for (i = 0; i <= b.length; i++) {
23730 for (j = 0; j <= a.length; j++) {
23734 for (i = 1; i <= b.length; i++) {
23735 for (j = 1; j <= a.length; j++) {
23736 if (b.charAt(i - 1) === a.charAt(j - 1)) {
23737 matrix[i][j] = matrix[i - 1][j - 1];
23739 matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
23740 Math.min(matrix[i][j - 1] + 1, // insertion
23741 matrix[i - 1][j] + 1)); // deletion
23746 return matrix[b.length][a.length];
23747 } // a d3.mouse-alike which
23748 // 1. Only works on HTML elements, not SVG
23749 // 2. Does not cause style recalculation
23751 function utilFastMouse(container) {
23752 var rect = container.getBoundingClientRect();
23753 var rectLeft = rect.left;
23754 var rectTop = rect.top;
23755 var clientLeft = +container.clientLeft;
23756 var clientTop = +container.clientTop;
23757 return function (e) {
23758 return [e.clientX - rectLeft - clientLeft, e.clientY - rectTop - clientTop];
23761 function utilAsyncMap(inputs, func, callback) {
23762 var remaining = inputs.length;
23765 inputs.forEach(function (d, i) {
23766 func(d, function done(err, data) {
23770 if (!remaining) callback(errors, results);
23773 } // wraps an index to an interval [0..length-1]
23775 function utilWrap(index, length) {
23777 index += Math.ceil(-index / length) * length;
23780 return index % length;
23783 * a replacement for functor
23785 * @param {*} value any value
23786 * @returns {Function} a function that returns that value or the value if it's a function
23789 function utilFunctor(value) {
23790 if (typeof value === 'function') return value;
23791 return function () {
23795 function utilNoAuto(selection) {
23796 var isText = selection.size() && selection.node().tagName.toLowerCase() === 'textarea';
23797 return selection // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
23798 .attr('autocomplete', 'new-password').attr('autocorrect', 'off').attr('autocapitalize', 'off').attr('spellcheck', isText ? 'true' : 'false');
23799 } // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
23800 // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
23802 function utilHashcode(str) {
23805 if (str.length === 0) {
23809 for (var i = 0; i < str.length; i++) {
23810 var _char = str.charCodeAt(i);
23812 hash = (hash << 5) - hash + _char;
23813 hash = hash & hash; // Convert to 32bit integer
23817 } // Returns version of `str` with all runs of special characters replaced by `_`;
23818 // suitable for HTML ids, classes, selectors, etc.
23820 function utilSafeClassName(str) {
23821 return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
23822 } // Returns string based on `val` that is highly unlikely to collide with an id
23823 // used previously or that's present elsewhere in the document. Useful for preventing
23824 // browser-provided autofills or when embedding iD on pages with unknown elements.
23826 function utilUniqueDomId(val) {
23827 return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
23828 } // Returns the length of `str` in unicode characters. This can be less than
23829 // `String.length()` since a single unicode character can be composed of multiple
23830 // JavaScript UTF-16 code units.
23832 function utilUnicodeCharsCount(str) {
23833 // Native ES2015 implementations of `Array.from` split strings into unicode characters
23834 return Array.from(str).length;
23835 } // Returns a new string representing `str` cut from its start to `limit` length
23836 // in unicode characters. Note that this runs the risk of splitting graphemes.
23838 function utilUnicodeCharsTruncated(str, limit) {
23839 return Array.from(str).slice(0, limit).join('');
23842 function osmEntity(attrs) {
23843 // For prototypal inheritance.
23844 if (this instanceof osmEntity) return; // Create the appropriate subtype.
23846 if (attrs && attrs.type) {
23847 return osmEntity[attrs.type].apply(this, arguments);
23848 } else if (attrs && attrs.id) {
23849 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
23850 } // Initialize a generic Entity (used only in tests).
23853 return new osmEntity().initialize(arguments);
23856 osmEntity.id = function (type) {
23857 return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
23860 osmEntity.id.next = {
23867 osmEntity.id.fromOSM = function (type, id) {
23868 return type[0] + id;
23871 osmEntity.id.toOSM = function (id) {
23872 return id.slice(1);
23875 osmEntity.id.type = function (id) {
23882 }; // A function suitable for use as the second argument to d3.selection#data().
23885 osmEntity.key = function (entity) {
23886 return entity.id + 'v' + (entity.v || 0);
23889 var _deprecatedTagValuesByKey;
23891 osmEntity.deprecatedTagValuesByKey = function (dataDeprecated) {
23892 if (!_deprecatedTagValuesByKey) {
23893 _deprecatedTagValuesByKey = {};
23894 dataDeprecated.forEach(function (d) {
23895 var oldKeys = Object.keys(d.old);
23897 if (oldKeys.length === 1) {
23898 var oldKey = oldKeys[0];
23899 var oldValue = d.old[oldKey];
23901 if (oldValue !== '*') {
23902 if (!_deprecatedTagValuesByKey[oldKey]) {
23903 _deprecatedTagValuesByKey[oldKey] = [oldValue];
23905 _deprecatedTagValuesByKey[oldKey].push(oldValue);
23912 return _deprecatedTagValuesByKey;
23915 osmEntity.prototype = {
23917 initialize: function initialize(sources) {
23918 for (var i = 0; i < sources.length; ++i) {
23919 var source = sources[i];
23921 for (var prop in source) {
23922 if (Object.prototype.hasOwnProperty.call(source, prop)) {
23923 if (source[prop] === undefined) {
23926 this[prop] = source[prop];
23932 if (!this.id && this.type) {
23933 this.id = osmEntity.id(this.type);
23936 if (!this.hasOwnProperty('visible')) {
23937 this.visible = true;
23942 copy: function copy(resolver, copies) {
23943 if (copies[this.id]) return copies[this.id];
23944 var copy = osmEntity(this, {
23949 copies[this.id] = copy;
23952 osmId: function osmId() {
23953 return osmEntity.id.toOSM(this.id);
23955 isNew: function isNew() {
23956 return this.osmId() < 0;
23958 update: function update(attrs) {
23959 return osmEntity(this, attrs, {
23960 v: 1 + (this.v || 0)
23963 mergeTags: function mergeTags(tags) {
23964 var merged = Object.assign({}, this.tags); // shallow copy
23966 var changed = false;
23968 for (var k in tags) {
23969 var t1 = merged[k];
23975 } else if (t1 !== t2) {
23977 merged[k] = utilUnicodeCharsTruncated(utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'), 255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
23982 return changed ? this.update({
23986 intersects: function intersects(extent, resolver) {
23987 return this.extent(resolver).intersects(extent);
23989 hasNonGeometryTags: function hasNonGeometryTags() {
23990 return Object.keys(this.tags).some(function (k) {
23991 return k !== 'area';
23994 hasParentRelations: function hasParentRelations(resolver) {
23995 return resolver.parentRelations(this).length > 0;
23997 hasInterestingTags: function hasInterestingTags() {
23998 return Object.keys(this.tags).some(osmIsInterestingTag);
24000 hasWikidata: function hasWikidata() {
24001 return !!this.tags.wikidata || !!this.tags['brand:wikidata'];
24003 isHighwayIntersection: function isHighwayIntersection() {
24006 isDegenerate: function isDegenerate() {
24009 deprecatedTags: function deprecatedTags(dataDeprecated) {
24010 var tags = this.tags; // if there are no tags, none can be deprecated
24012 if (Object.keys(tags).length === 0) return [];
24013 var deprecated = [];
24014 dataDeprecated.forEach(function (d) {
24015 var oldKeys = Object.keys(d.old);
24018 var hasExistingValues = Object.keys(d.replace).some(function (replaceKey) {
24019 if (!tags[replaceKey] || d.old[replaceKey]) return false;
24020 var replaceValue = d.replace[replaceKey];
24021 if (replaceValue === '*') return false;
24022 if (replaceValue === tags[replaceKey]) return false;
24024 }); // don't flag deprecated tags if the upgrade path would overwrite existing data - #7843
24026 if (hasExistingValues) return;
24029 var matchesDeprecatedTags = oldKeys.every(function (oldKey) {
24030 if (!tags[oldKey]) return false;
24031 if (d.old[oldKey] === '*') return true;
24032 if (d.old[oldKey] === tags[oldKey]) return true;
24033 var vals = tags[oldKey].split(';').filter(Boolean);
24035 if (vals.length === 0) {
24037 } else if (vals.length > 1) {
24038 return vals.indexOf(d.old[oldKey]) !== -1;
24040 if (tags[oldKey] === d.old[oldKey]) {
24041 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
24042 var replaceKeys = Object.keys(d.replace);
24043 return !replaceKeys.every(function (replaceKey) {
24044 return tags[replaceKey] === d.replace[replaceKey];
24055 if (matchesDeprecatedTags) {
24056 deprecated.push(d);
24063 function osmLanes(entity) {
24064 if (entity.type !== 'way') return null;
24065 if (!entity.tags.highway) return null;
24066 var tags = entity.tags;
24067 var isOneWay = entity.isOneWay();
24068 var laneCount = getLaneCount(tags, isOneWay);
24069 var maxspeed = parseMaxspeed(tags);
24070 var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
24071 var forward = laneDirections.forward;
24072 var backward = laneDirections.backward;
24073 var bothways = laneDirections.bothways; // parse the piped string 'x|y|z' format
24075 var turnLanes = {};
24076 turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
24077 turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
24078 turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
24079 var maxspeedLanes = {};
24080 maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
24081 maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
24082 maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
24084 psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
24085 psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
24086 psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
24088 busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
24089 busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
24090 busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
24091 var taxiLanes = {};
24092 taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
24093 taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
24094 taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
24096 hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
24097 hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
24098 hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
24100 hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
24101 hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
24102 hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
24103 var bicyclewayLanes = {};
24104 bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
24105 bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
24106 bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
24111 }; // map forward/backward/unspecified of each lane type to lanesObj
24113 mapToLanesObj(lanesObj, turnLanes, 'turnLane');
24114 mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
24115 mapToLanesObj(lanesObj, psvLanes, 'psv');
24116 mapToLanesObj(lanesObj, busLanes, 'bus');
24117 mapToLanesObj(lanesObj, taxiLanes, 'taxi');
24118 mapToLanesObj(lanesObj, hovLanes, 'hov');
24119 mapToLanesObj(lanesObj, hgvLanes, 'hgv');
24120 mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
24126 backward: backward,
24127 bothways: bothways,
24128 turnLanes: turnLanes,
24129 maxspeed: maxspeed,
24130 maxspeedLanes: maxspeedLanes,
24131 psvLanes: psvLanes,
24132 busLanes: busLanes,
24133 taxiLanes: taxiLanes,
24134 hovLanes: hovLanes,
24135 hgvLanes: hgvLanes,
24136 bicyclewayLanes: bicyclewayLanes
24142 function getLaneCount(tags, isOneWay) {
24146 count = parseInt(tags.lanes, 10);
24153 switch (tags.highway) {
24156 count = isOneWay ? 2 : 4;
24160 count = isOneWay ? 1 : 2;
24167 function parseMaxspeed(tags) {
24168 var maxspeed = tags.maxspeed;
24169 if (!maxspeed) return;
24170 var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
24171 if (!maxspeedRegex.test(maxspeed)) return;
24172 return parseInt(maxspeed, 10);
24175 function parseLaneDirections(tags, isOneWay, laneCount) {
24176 var forward = parseInt(tags['lanes:forward'], 10);
24177 var backward = parseInt(tags['lanes:backward'], 10);
24178 var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
24180 if (parseInt(tags.oneway, 10) === -1) {
24183 backward = laneCount;
24184 } else if (isOneWay) {
24185 forward = laneCount;
24188 } else if (isNaN(forward) && isNaN(backward)) {
24189 backward = Math.floor((laneCount - bothways) / 2);
24190 forward = laneCount - bothways - backward;
24191 } else if (isNaN(forward)) {
24192 if (backward > laneCount - bothways) {
24193 backward = laneCount - bothways;
24196 forward = laneCount - bothways - backward;
24197 } else if (isNaN(backward)) {
24198 if (forward > laneCount - bothways) {
24199 forward = laneCount - bothways;
24202 backward = laneCount - bothways - forward;
24207 backward: backward,
24212 function parseTurnLanes(tag) {
24214 var validValues = ['left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right', 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'];
24215 return tag.split('|').map(function (s) {
24216 if (s === '') s = 'none';
24217 return s.split(';').map(function (d) {
24218 return validValues.indexOf(d) === -1 ? 'unknown' : d;
24223 function parseMaxspeedLanes(tag, maxspeed) {
24225 return tag.split('|').map(function (s) {
24226 if (s === 'none') return s;
24227 var m = parseInt(s, 10);
24228 if (s === '' || m === maxspeed) return null;
24229 return isNaN(m) ? 'unknown' : m;
24233 function parseMiscLanes(tag) {
24235 var validValues = ['yes', 'no', 'designated'];
24236 return tag.split('|').map(function (s) {
24237 if (s === '') s = 'no';
24238 return validValues.indexOf(s) === -1 ? 'unknown' : s;
24242 function parseBicycleWay(tag) {
24244 var validValues = ['yes', 'no', 'designated', 'lane'];
24245 return tag.split('|').map(function (s) {
24246 if (s === '') s = 'no';
24247 return validValues.indexOf(s) === -1 ? 'unknown' : s;
24251 function mapToLanesObj(lanesObj, data, key) {
24252 if (data.forward) data.forward.forEach(function (l, i) {
24253 if (!lanesObj.forward[i]) lanesObj.forward[i] = {};
24254 lanesObj.forward[i][key] = l;
24256 if (data.backward) data.backward.forEach(function (l, i) {
24257 if (!lanesObj.backward[i]) lanesObj.backward[i] = {};
24258 lanesObj.backward[i][key] = l;
24260 if (data.unspecified) data.unspecified.forEach(function (l, i) {
24261 if (!lanesObj.unspecified[i]) lanesObj.unspecified[i] = {};
24262 lanesObj.unspecified[i][key] = l;
24266 function osmWay() {
24267 if (!(this instanceof osmWay)) {
24268 return new osmWay().initialize(arguments);
24269 } else if (arguments.length) {
24270 this.initialize(arguments);
24273 osmEntity.way = osmWay;
24274 osmWay.prototype = Object.create(osmEntity.prototype);
24275 Object.assign(osmWay.prototype, {
24278 copy: function copy(resolver, copies) {
24279 if (copies[this.id]) return copies[this.id];
24280 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
24281 var nodes = this.nodes.map(function (id) {
24282 return resolver.entity(id).copy(resolver, copies).id;
24284 copy = copy.update({
24287 copies[this.id] = copy;
24290 extent: function extent(resolver) {
24291 return resolver["transient"](this, 'extent', function () {
24292 var extent = geoExtent();
24294 for (var i = 0; i < this.nodes.length; i++) {
24295 var node = resolver.hasEntity(this.nodes[i]);
24298 extent._extend(node.extent());
24305 first: function first() {
24306 return this.nodes[0];
24308 last: function last() {
24309 return this.nodes[this.nodes.length - 1];
24311 contains: function contains(node) {
24312 return this.nodes.indexOf(node) >= 0;
24314 affix: function affix(node) {
24315 if (this.nodes[0] === node) return 'prefix';
24316 if (this.nodes[this.nodes.length - 1] === node) return 'suffix';
24318 layer: function layer() {
24319 // explicit layer tag, clamp between -10, 10..
24320 if (isFinite(this.tags.layer)) {
24321 return Math.max(-10, Math.min(+this.tags.layer, 10));
24322 } // implied layer tag..
24325 if (this.tags.covered === 'yes') return -1;
24326 if (this.tags.location === 'overground') return 1;
24327 if (this.tags.location === 'underground') return -1;
24328 if (this.tags.location === 'underwater') return -10;
24329 if (this.tags.power === 'line') return 10;
24330 if (this.tags.power === 'minor_line') return 10;
24331 if (this.tags.aerialway) return 10;
24332 if (this.tags.bridge) return 1;
24333 if (this.tags.cutting) return -1;
24334 if (this.tags.tunnel) return -1;
24335 if (this.tags.waterway) return -1;
24336 if (this.tags.man_made === 'pipeline') return -10;
24337 if (this.tags.boundary) return -10;
24340 // the approximate width of the line based on its tags except its `width` tag
24341 impliedLineWidthMeters: function impliedLineWidthMeters() {
24342 var averageWidths = {
24344 // width is for single lane
24371 // width includes ties and rail bed, not just track gauge
24394 for (var key in averageWidths) {
24395 if (this.tags[key] && averageWidths[key][this.tags[key]]) {
24396 var width = averageWidths[key][this.tags[key]];
24398 if (key === 'highway') {
24399 var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
24400 if (!laneCount) laneCount = this.isOneWay() ? 1 : 2;
24401 return width * laneCount;
24410 isOneWay: function isOneWay() {
24411 // explicit oneway tag..
24416 'reversible': true,
24417 'alternating': true,
24422 if (values[this.tags.oneway] !== undefined) {
24423 return values[this.tags.oneway];
24424 } // implied oneway tag..
24427 for (var key in this.tags) {
24428 if (key in osmOneWayTags && this.tags[key] in osmOneWayTags[key]) return true;
24433 // Some identifier for tag that implies that this way is "sided",
24434 // i.e. the right side is the 'inside' (e.g. the right side of a
24435 // natural=cliff is lower).
24436 sidednessIdentifier: function sidednessIdentifier() {
24437 for (var key in this.tags) {
24438 var value = this.tags[key];
24440 if (key in osmRightSideIsInsideTags && value in osmRightSideIsInsideTags[key]) {
24441 if (osmRightSideIsInsideTags[key][value] === true) {
24444 // if the map's value is something other than a
24445 // literal true, we should use it so we can
24446 // special case some keys (e.g. natural=coastline
24447 // is handled differently to other naturals).
24448 return osmRightSideIsInsideTags[key][value];
24455 isSided: function isSided() {
24456 if (this.tags.two_sided === 'yes') {
24460 return this.sidednessIdentifier() !== null;
24462 lanes: function lanes() {
24463 return osmLanes(this);
24465 isClosed: function isClosed() {
24466 return this.nodes.length > 1 && this.first() === this.last();
24468 isConvex: function isConvex(resolver) {
24469 if (!this.isClosed() || this.isDegenerate()) return null;
24470 var nodes = utilArrayUniq(resolver.childNodes(this));
24471 var coords = nodes.map(function (n) {
24477 for (var i = 0; i < coords.length; i++) {
24478 var o = coords[(i + 1) % coords.length];
24480 var b = coords[(i + 2) % coords.length];
24481 var res = geoVecCross(a, b, o);
24482 curr = res > 0 ? 1 : res < 0 ? -1 : 0;
24486 } else if (prev && curr !== prev) {
24495 // returns an object with the tag that implies this is an area, if any
24496 tagSuggestingArea: function tagSuggestingArea() {
24497 return osmTagSuggestingArea(this.tags);
24499 isArea: function isArea() {
24500 if (this.tags.area === 'yes') return true;
24501 if (!this.isClosed() || this.tags.area === 'no') return false;
24502 return this.tagSuggestingArea() !== null;
24504 isDegenerate: function isDegenerate() {
24505 return new Set(this.nodes).size < (this.isArea() ? 3 : 2);
24507 areAdjacent: function areAdjacent(n1, n2) {
24508 for (var i = 0; i < this.nodes.length; i++) {
24509 if (this.nodes[i] === n1) {
24510 if (this.nodes[i - 1] === n2) return true;
24511 if (this.nodes[i + 1] === n2) return true;
24517 geometry: function geometry(graph) {
24518 return graph["transient"](this, 'geometry', function () {
24519 return this.isArea() ? 'area' : 'line';
24522 // returns an array of objects representing the segments between the nodes in this way
24523 segments: function segments(graph) {
24524 function segmentExtent(graph) {
24525 var n1 = graph.hasEntity(this.nodes[0]);
24526 var n2 = graph.hasEntity(this.nodes[1]);
24527 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])]]);
24530 return graph["transient"](this, 'segments', function () {
24533 for (var i = 0; i < this.nodes.length - 1; i++) {
24535 id: this.id + '-' + i,
24538 nodes: [this.nodes[i], this.nodes[i + 1]],
24539 extent: segmentExtent
24546 // If this way is not closed, append the beginning node to the end of the nodelist to close it.
24547 close: function close() {
24548 if (this.isClosed() || !this.nodes.length) return this;
24549 var nodes = this.nodes.slice();
24550 nodes = nodes.filter(noRepeatNodes);
24551 nodes.push(nodes[0]);
24552 return this.update({
24556 // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
24557 unclose: function unclose() {
24558 if (!this.isClosed()) return this;
24559 var nodes = this.nodes.slice();
24560 var connector = this.first();
24561 var i = nodes.length - 1; // remove trailing connectors..
24563 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24564 nodes.splice(i, 1);
24565 i = nodes.length - 1;
24568 nodes = nodes.filter(noRepeatNodes);
24569 return this.update({
24573 // Adds a node (id) in front of the node which is currently at position index.
24574 // If index is undefined, the node will be added to the end of the way for linear ways,
24575 // or just before the final connecting node for circular ways.
24576 // Consecutive duplicates are eliminated including existing ones.
24577 // Circularity is always preserved when adding a node.
24578 addNode: function addNode(id, index) {
24579 var nodes = this.nodes.slice();
24580 var isClosed = this.isClosed();
24581 var max = isClosed ? nodes.length - 1 : nodes.length;
24583 if (index === undefined) {
24587 if (index < 0 || index > max) {
24588 throw new RangeError('index ' + index + ' out of range 0..' + max);
24589 } // If this is a closed way, remove all connector nodes except the first one
24590 // (there may be duplicates) and adjust index if necessary..
24594 var connector = this.first(); // leading connectors..
24598 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
24599 nodes.splice(i, 1);
24600 if (index > i) index--;
24601 } // trailing connectors..
24604 i = nodes.length - 1;
24606 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24607 nodes.splice(i, 1);
24608 if (index > i) index--;
24609 i = nodes.length - 1;
24613 nodes.splice(index, 0, id);
24614 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24616 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24617 nodes.push(nodes[0]);
24620 return this.update({
24624 // Replaces the node which is currently at position index with the given node (id).
24625 // Consecutive duplicates are eliminated including existing ones.
24626 // Circularity is preserved when updating a node.
24627 updateNode: function updateNode(id, index) {
24628 var nodes = this.nodes.slice();
24629 var isClosed = this.isClosed();
24630 var max = nodes.length - 1;
24632 if (index === undefined || index < 0 || index > max) {
24633 throw new RangeError('index ' + index + ' out of range 0..' + max);
24634 } // If this is a closed way, remove all connector nodes except the first one
24635 // (there may be duplicates) and adjust index if necessary..
24639 var connector = this.first(); // leading connectors..
24643 while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
24644 nodes.splice(i, 1);
24645 if (index > i) index--;
24646 } // trailing connectors..
24649 i = nodes.length - 1;
24651 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
24652 nodes.splice(i, 1);
24653 if (index === i) index = 0; // update leading connector instead
24655 i = nodes.length - 1;
24659 nodes.splice(index, 1, id);
24660 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24662 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24663 nodes.push(nodes[0]);
24666 return this.update({
24670 // Replaces each occurrence of node id needle with replacement.
24671 // Consecutive duplicates are eliminated including existing ones.
24672 // Circularity is preserved.
24673 replaceNode: function replaceNode(needleID, replacementID) {
24674 var nodes = this.nodes.slice();
24675 var isClosed = this.isClosed();
24677 for (var i = 0; i < nodes.length; i++) {
24678 if (nodes[i] === needleID) {
24679 nodes[i] = replacementID;
24683 nodes = nodes.filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24685 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24686 nodes.push(nodes[0]);
24689 return this.update({
24693 // Removes each occurrence of node id.
24694 // Consecutive duplicates are eliminated including existing ones.
24695 // Circularity is preserved.
24696 removeNode: function removeNode(id) {
24697 var nodes = this.nodes.slice();
24698 var isClosed = this.isClosed();
24699 nodes = nodes.filter(function (node) {
24700 return node !== id;
24701 }).filter(noRepeatNodes); // If the way was closed before, append a connector node to keep it closed..
24703 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
24704 nodes.push(nodes[0]);
24707 return this.update({
24711 asJXON: function asJXON(changeset_id) {
24714 '@id': this.osmId(),
24715 '@version': this.version || 0,
24716 nd: this.nodes.map(function (id) {
24719 ref: osmEntity.id.toOSM(id)
24723 tag: Object.keys(this.tags).map(function (k) {
24734 if (changeset_id) {
24735 r.way['@changeset'] = changeset_id;
24740 asGeoJSON: function asGeoJSON(resolver) {
24741 return resolver["transient"](this, 'GeoJSON', function () {
24742 var coordinates = resolver.childNodes(this).map(function (n) {
24746 if (this.isArea() && this.isClosed()) {
24749 coordinates: [coordinates]
24753 type: 'LineString',
24754 coordinates: coordinates
24759 area: function area(resolver) {
24760 return resolver["transient"](this, 'area', function () {
24761 var nodes = resolver.childNodes(this);
24764 coordinates: [nodes.map(function (n) {
24769 if (!this.isClosed() && nodes.length) {
24770 json.coordinates[0].push(nodes[0].loc);
24773 var area = d3_geoArea(json); // Heuristic for detecting counterclockwise winding order. Assumes
24774 // that OpenStreetMap polygons are not hemisphere-spanning.
24776 if (area > 2 * Math.PI) {
24777 json.coordinates[0] = json.coordinates[0].reverse();
24778 area = d3_geoArea(json);
24781 return isNaN(area) ? 0 : area;
24784 }); // Filter function to eliminate consecutive duplicates.
24786 function noRepeatNodes(node, i, arr) {
24787 return i === 0 || node !== arr[i - 1];
24791 // 1. Relation tagged with `type=multipolygon` and no interesting tags.
24792 // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
24793 // 3. No members without a role.
24795 // Old multipolygons are no longer recommended but are still rendered as areas by iD.
24797 function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
24798 if (entity.type !== 'relation' || !entity.isMultipolygon() || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
24804 for (var memberIndex in entity.members) {
24805 var member = entity.members[memberIndex];
24807 if (!member.role || member.role === 'outer') {
24808 if (outerMember) return false;
24809 if (member.type !== 'way') return false;
24810 if (!graph.hasEntity(member.id)) return false;
24811 outerMember = graph.entity(member.id);
24813 if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
24819 return outerMember;
24820 } // For fixing up rendering of multipolygons with tags on the outer member.
24821 // https://github.com/openstreetmap/iD/issues/613
24823 function osmIsOldMultipolygonOuterMember(entity, graph) {
24824 if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0) return false;
24825 var parents = graph.parentRelations(entity);
24826 if (parents.length !== 1) return false;
24827 var parent = parents[0];
24828 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) return false;
24829 var members = parent.members,
24832 for (var i = 0; i < members.length; i++) {
24833 member = members[i];
24834 if (member.id === entity.id && member.role && member.role !== 'outer') return false; // Not outer member
24836 if (member.id !== entity.id && (!member.role || member.role === 'outer')) return false; // Not a simple multipolygon
24841 function osmOldMultipolygonOuterMember(entity, graph) {
24842 if (entity.type !== 'way') return false;
24843 var parents = graph.parentRelations(entity);
24844 if (parents.length !== 1) return false;
24845 var parent = parents[0];
24846 if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1) return false;
24847 var members = parent.members,
24851 for (var i = 0; i < members.length; i++) {
24852 member = members[i];
24854 if (!member.role || member.role === 'outer') {
24855 if (outerMember) return false; // Not a simple multipolygon
24857 outerMember = member;
24861 if (!outerMember) return false;
24862 var outerEntity = graph.hasEntity(outerMember.id);
24863 if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length) return false;
24864 return outerEntity;
24865 } // Join `toJoin` array into sequences of connecting ways.
24866 // Segments which share identical start/end nodes will, as much as possible,
24867 // be connected with each other.
24869 // The return value is a nested array. Each constituent array contains elements
24870 // of `toJoin` which have been determined to connect.
24872 // Each consitituent array also has a `nodes` property whose value is an
24873 // ordered array of member nodes, with appropriate order reversal and
24874 // start/end coordinate de-duplication.
24876 // Members of `toJoin` must have, at minimum, `type` and `id` properties.
24877 // Thus either an array of `osmWay`s or a relation member array may be used.
24879 // If an member is an `osmWay`, its tags and childnodes may be reversed via
24880 // `actionReverse` in the output.
24882 // The returned sequences array also has an `actions` array property, containing
24883 // any reversal actions that should be applied to the graph, should the calling
24884 // code attempt to actually join the given ways.
24886 // Incomplete members (those for which `graph.hasEntity(element.id)` returns
24887 // false) and non-way members are ignored.
24890 function osmJoinWays(toJoin, graph) {
24891 function resolve(member) {
24892 return graph.childNodes(graph.entity(member.id));
24895 function reverse(item) {
24896 var action = actionReverse(item.id, {
24897 reverseOneway: true
24899 sequences.actions.push(action);
24900 return item instanceof osmWay ? action(graph).entity(item.id) : item;
24901 } // make a copy containing only the items to join
24904 toJoin = toJoin.filter(function (member) {
24905 return member.type === 'way' && graph.hasEntity(member.id);
24906 }); // Are the things we are joining relation members or `osmWays`?
24907 // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
24910 var joinAsMembers = true;
24912 for (i = 0; i < toJoin.length; i++) {
24913 if (toJoin[i] instanceof osmWay) {
24914 joinAsMembers = false;
24919 var sequences = [];
24920 sequences.actions = [];
24922 while (toJoin.length) {
24923 // start a new sequence
24924 var item = toJoin.shift();
24925 var currWays = [item];
24926 var currNodes = resolve(item).slice(); // add to it
24928 while (toJoin.length) {
24929 var start = currNodes[0];
24930 var end = currNodes[currNodes.length - 1];
24932 var nodes = null; // Find the next way/member to join.
24934 for (i = 0; i < toJoin.length; i++) {
24936 nodes = resolve(item); // (for member ordering only, not way ordering - see #4872)
24937 // Strongly prefer to generate a forward path that preserves the order
24938 // of the members array. For multipolygons and most relations, member
24939 // order does not matter - but for routes, it does. (see #4589)
24940 // If we started this sequence backwards (i.e. next member way attaches to
24941 // the start node and not the end node), reverse the initial way before continuing.
24943 if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end && (nodes[nodes.length - 1] === start || nodes[0] === start)) {
24944 currWays[0] = reverse(currWays[0]);
24945 currNodes.reverse();
24946 start = currNodes[0];
24947 end = currNodes[currNodes.length - 1];
24950 if (nodes[0] === end) {
24951 fn = currNodes.push; // join to end
24953 nodes = nodes.slice(1);
24955 } else if (nodes[nodes.length - 1] === end) {
24956 fn = currNodes.push; // join to end
24958 nodes = nodes.slice(0, -1).reverse();
24959 item = reverse(item);
24961 } else if (nodes[nodes.length - 1] === start) {
24962 fn = currNodes.unshift; // join to beginning
24964 nodes = nodes.slice(0, -1);
24966 } else if (nodes[0] === start) {
24967 fn = currNodes.unshift; // join to beginning
24969 nodes = nodes.slice(1).reverse();
24970 item = reverse(item);
24978 // couldn't find a joinable way/member
24982 fn.apply(currWays, [item]);
24983 fn.apply(currNodes, nodes);
24984 toJoin.splice(i, 1);
24987 currWays.nodes = currNodes;
24988 sequences.push(currWays);
24994 function actionAddMember(relationId, member, memberIndex, insertPair) {
24995 return function action(graph) {
24996 var relation = graph.entity(relationId); // There are some special rules for Public Transport v2 routes.
24998 var isPTv2 = /stop|platform/.test(member.role);
25000 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
25001 // Try to perform sensible inserts based on how the ways join together
25002 graph = addWayMember(relation, graph);
25004 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
25005 // Stops and Platforms for PTv2 should be ordered first.
25006 // hack: We do not currently have the ability to place them in the exactly correct order.
25007 if (isPTv2 && isNaN(memberIndex)) {
25011 graph = graph.replace(relation.addMember(member, memberIndex));
25015 }; // Add a way member into the relation "wherever it makes sense".
25016 // In this situation we were not supplied a memberIndex.
25018 function addWayMember(relation, graph) {
25019 var groups, tempWay, item, i, j, k; // remove PTv2 stops and platforms before doing anything.
25021 var PTv2members = [];
25024 for (i = 0; i < relation.members.length; i++) {
25025 var m = relation.members[i];
25027 if (/stop|platform/.test(m.role)) {
25028 PTv2members.push(m);
25034 relation = relation.update({
25039 // We're adding a member that must stay paired with an existing member.
25040 // (This feature is used by `actionSplit`)
25042 // This is tricky because the members may exist multiple times in the
25043 // member list, and with different A-B/B-A ordering and different roles.
25044 // (e.g. a bus route that loops out and back - #4589).
25046 // Replace the existing member with a temporary way,
25047 // so that `osmJoinWays` can treat the pair like a single way.
25050 nodes: insertPair.nodes
25052 graph = graph.replace(tempWay);
25058 var tempRelation = relation.replaceMember({
25059 id: insertPair.originalID
25060 }, tempMember, true);
25061 groups = utilArrayGroupBy(tempRelation.members, 'type');
25062 groups.way = groups.way || [];
25064 // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
25065 groups = utilArrayGroupBy(relation.members, 'type');
25066 groups.way = groups.way || [];
25067 groups.way.push(member);
25070 members = withIndex(groups.way);
25071 var joined = osmJoinWays(members, graph); // `joined` might not contain all of the way members,
25072 // But will contain only the completed (downloaded) members
25074 for (i = 0; i < joined.length; i++) {
25075 var segment = joined[i];
25076 var nodes = segment.nodes.slice();
25077 var startIndex = segment[0].index; // j = array index in `members` where this segment starts
25079 for (j = 0; j < members.length; j++) {
25080 if (members[j].index === startIndex) {
25083 } // k = each member in segment
25086 for (k = 0; k < segment.length; k++) {
25088 var way = graph.entity(item.id); // If this is a paired item, generate members in correct order and role
25090 if (tempWay && item.id === tempWay.id) {
25091 if (nodes[0].id === insertPair.nodes[0]) {
25093 id: insertPair.originalID,
25097 id: insertPair.insertedID,
25103 id: insertPair.insertedID,
25107 id: insertPair.originalID,
25112 } // reorder `members` if necessary
25116 if (j + k >= members.length || item.index !== members[j + k].index) {
25117 moveMember(members, item.index, j + k);
25121 nodes.splice(0, way.nodes.length - 1);
25126 graph = graph.remove(tempWay);
25127 } // Final pass: skip dead items, split pairs, remove index properties
25130 var wayMembers = [];
25132 for (i = 0; i < members.length; i++) {
25134 if (item.index === -1) continue;
25137 wayMembers.push(item.pair[0]);
25138 wayMembers.push(item.pair[1]);
25140 wayMembers.push(utilObjectOmit(item, ['index']));
25142 } // Put stops and platforms first, then nodes, ways, relations
25143 // This is recommended for Public Transport v2 routes:
25144 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
25147 var newMembers = PTv2members.concat(groups.node || [], wayMembers, groups.relation || []);
25148 return graph.replace(relation.update({
25149 members: newMembers
25150 })); // `moveMember()` changes the `members` array in place by splicing
25151 // the item with `.index = findIndex` to where it belongs,
25152 // and marking the old position as "dead" with `.index = -1`
25156 // members 0 1 2 3 4 5 6 7 8 9 keep 5 in j+k
25160 // members 0 1 2 3 4 5 6 7 8 9 move 4 to j+k
25161 // members 0 1 2 3 x 5 4 6 7 8 9 moved
25165 // members 0 1 2 3 x 5 4 6 7 8 9 move 7 to j+k
25166 // members 0 1 2 3 x 5 4 7 6 x 8 9 moved
25170 // members 0 1 2 3 x 5 4 7 6 x 8 9 keep 6 in j+k
25173 function moveMember(arr, findIndex, toIndex) {
25176 for (i = 0; i < arr.length; i++) {
25177 if (arr[i].index === findIndex) {
25182 var item = Object.assign({}, arr[i]); // shallow copy
25184 arr[i].index = -1; // mark as dead
25186 item.index = toIndex;
25187 arr.splice(toIndex, 0, item);
25188 } // This is the same as `Relation.indexedMembers`,
25189 // Except we don't want to index all the members, only the ways
25192 function withIndex(arr) {
25193 var result = new Array(arr.length);
25195 for (var i = 0; i < arr.length; i++) {
25196 result[i] = Object.assign({}, arr[i]); // shallow copy
25198 result[i].index = i;
25206 function actionAddMidpoint(midpoint, node) {
25207 return function (graph) {
25208 graph = graph.replace(node.move(midpoint.loc));
25209 var parents = utilArrayIntersection(graph.parentWays(graph.entity(midpoint.edge[0])), graph.parentWays(graph.entity(midpoint.edge[1])));
25210 parents.forEach(function (way) {
25211 for (var i = 0; i < way.nodes.length - 1; i++) {
25212 if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
25213 graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1)); // Add only one midpoint on doubled-back segments,
25214 // turning them into self-intersections.
25224 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
25225 function actionAddVertex(wayId, nodeId, index) {
25226 return function (graph) {
25227 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
25231 function actionChangeMember(relationId, member, memberIndex) {
25232 return function (graph) {
25233 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
25237 function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
25238 return function action(graph) {
25239 var entity = graph.entity(entityID);
25240 var geometry = entity.geometry(graph);
25241 var tags = entity.tags;
25242 if (oldPreset) tags = oldPreset.unsetTags(tags, geometry);
25243 if (newPreset) tags = newPreset.setTags(tags, geometry, skipFieldDefaults);
25244 return graph.replace(entity.update({
25250 function actionChangeTags(entityId, tags) {
25251 return function (graph) {
25252 var entity = graph.entity(entityId);
25253 return graph.replace(entity.update({
25259 function osmNode() {
25260 if (!(this instanceof osmNode)) {
25261 return new osmNode().initialize(arguments);
25262 } else if (arguments.length) {
25263 this.initialize(arguments);
25266 osmEntity.node = osmNode;
25267 osmNode.prototype = Object.create(osmEntity.prototype);
25268 Object.assign(osmNode.prototype, {
25271 extent: function extent() {
25272 return new geoExtent(this.loc);
25274 geometry: function geometry(graph) {
25275 return graph["transient"](this, 'geometry', function () {
25276 return graph.isPoi(this) ? 'point' : 'vertex';
25279 move: function move(loc) {
25280 return this.update({
25284 isDegenerate: function isDegenerate() {
25285 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);
25287 // Inspect tags and geometry to determine which direction(s) this node/vertex points
25288 directions: function directions(resolver, projection) {
25290 var i; // which tag to use?
25292 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
25293 // all-way stop tag on a highway intersection
25296 // generic direction tag
25297 val = (this.tags.direction || '').toLowerCase(); // better suffix-style direction tag
25299 var re = /:direction$/i;
25300 var keys = Object.keys(this.tags);
25302 for (i = 0; i < keys.length; i++) {
25303 if (re.test(keys[i])) {
25304 val = this.tags[keys[i]].toLowerCase();
25310 if (val === '') return [];
25314 northnortheast: 22,
25322 eastsoutheast: 112,
25326 southsoutheast: 157,
25330 southsouthwest: 202,
25334 westsouthwest: 247,
25338 westnorthwest: 292,
25342 northnorthwest: 337,
25345 var values = val.split(';');
25347 values.forEach(function (v) {
25348 // swap cardinal for numeric directions
25349 if (cardinal[v] !== undefined) {
25351 } // numeric direction - just add to results
25354 if (v !== '' && !isNaN(+v)) {
25357 } // string direction - inspect parent ways
25360 var lookBackward = this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all';
25361 var lookForward = this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all';
25362 if (!lookForward && !lookBackward) return;
25364 resolver.parentWays(this).forEach(function (parent) {
25365 var nodes = parent.nodes;
25367 for (i = 0; i < nodes.length; i++) {
25368 if (nodes[i] === this.id) {
25369 // match current entity
25370 if (lookForward && i > 0) {
25371 nodeIds[nodes[i - 1]] = true; // look back to prev node
25374 if (lookBackward && i < nodes.length - 1) {
25375 nodeIds[nodes[i + 1]] = true; // look ahead to next node
25380 Object.keys(nodeIds).forEach(function (nodeId) {
25381 // +90 because geoAngle returns angle from X axis, not Y (north)
25382 results.push(geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI) + 90);
25385 return utilArrayUniq(results);
25387 isEndpoint: function isEndpoint(resolver) {
25388 return resolver["transient"](this, 'isEndpoint', function () {
25390 return resolver.parentWays(this).filter(function (parent) {
25391 return !parent.isClosed() && !!parent.affix(id);
25395 isConnected: function isConnected(resolver) {
25396 return resolver["transient"](this, 'isConnected', function () {
25397 var parents = resolver.parentWays(this);
25399 if (parents.length > 1) {
25400 // vertex is connected to multiple parent ways
25401 for (var i in parents) {
25402 if (parents[i].geometry(resolver) === 'line' && parents[i].hasInterestingTags()) return true;
25404 } else if (parents.length === 1) {
25405 var way = parents[0];
25406 var nodes = way.nodes.slice();
25408 if (way.isClosed()) {
25410 } // ignore connecting node if closed
25411 // return true if vertex appears multiple times (way is self intersecting)
25414 return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
25420 parentIntersectionWays: function parentIntersectionWays(resolver) {
25421 return resolver["transient"](this, 'parentIntersectionWays', function () {
25422 return resolver.parentWays(this).filter(function (parent) {
25423 return (parent.tags.highway || parent.tags.waterway || parent.tags.railway || parent.tags.aeroway) && parent.geometry(resolver) === 'line';
25427 isIntersection: function isIntersection(resolver) {
25428 return this.parentIntersectionWays(resolver).length > 1;
25430 isHighwayIntersection: function isHighwayIntersection(resolver) {
25431 return resolver["transient"](this, 'isHighwayIntersection', function () {
25432 return resolver.parentWays(this).filter(function (parent) {
25433 return parent.tags.highway && parent.geometry(resolver) === 'line';
25437 isOnAddressLine: function isOnAddressLine(resolver) {
25438 return resolver["transient"](this, 'isOnAddressLine', function () {
25439 return resolver.parentWays(this).filter(function (parent) {
25440 return parent.tags.hasOwnProperty('addr:interpolation') && parent.geometry(resolver) === 'line';
25444 asJXON: function asJXON(changeset_id) {
25447 '@id': this.osmId(),
25448 '@lon': this.loc[0],
25449 '@lat': this.loc[1],
25450 '@version': this.version || 0,
25451 tag: Object.keys(this.tags).map(function (k) {
25461 if (changeset_id) r.node['@changeset'] = changeset_id;
25464 asGeoJSON: function asGeoJSON() {
25467 coordinates: this.loc
25472 function actionCircularize(wayId, projection, maxAngle) {
25473 maxAngle = (maxAngle || 20) * Math.PI / 180;
25475 var action = function action(graph, t) {
25476 if (t === null || !isFinite(t)) t = 1;
25477 t = Math.min(Math.max(+t, 0), 1);
25478 var way = graph.entity(wayId);
25479 var origNodes = {};
25480 graph.childNodes(way).forEach(function (node) {
25481 if (!origNodes[node.id]) origNodes[node.id] = node;
25484 if (!way.isConvex(graph)) {
25485 graph = action.makeConvex(graph);
25488 var nodes = utilArrayUniq(graph.childNodes(way));
25489 var keyNodes = nodes.filter(function (n) {
25490 return graph.parentWays(n).length !== 1;
25492 var points = nodes.map(function (n) {
25493 return projection(n.loc);
25495 var keyPoints = keyNodes.map(function (n) {
25496 return projection(n.loc);
25498 var centroid = points.length === 2 ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
25499 var radius = d3_median(points, function (p) {
25500 return geoVecLength(centroid, p);
25502 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
25503 var ids, i, j, k; // we need at least two key nodes for the algorithm to work
25505 if (!keyNodes.length) {
25506 keyNodes = [nodes[0]];
25507 keyPoints = [points[0]];
25510 if (keyNodes.length === 1) {
25511 var index = nodes.indexOf(keyNodes[0]);
25512 var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
25513 keyNodes.push(nodes[oppositeIndex]);
25514 keyPoints.push(points[oppositeIndex]);
25515 } // key points and nodes are those connected to the ways,
25516 // they are projected onto the circle, in between nodes are moved
25517 // to constant intervals between key nodes, extra in between nodes are
25518 // added if necessary.
25521 for (i = 0; i < keyPoints.length; i++) {
25522 var nextKeyNodeIndex = (i + 1) % keyNodes.length;
25523 var startNode = keyNodes[i];
25524 var endNode = keyNodes[nextKeyNodeIndex];
25525 var startNodeIndex = nodes.indexOf(startNode);
25526 var endNodeIndex = nodes.indexOf(endNode);
25527 var numberNewPoints = -1;
25528 var indexRange = endNodeIndex - startNodeIndex;
25529 var nearNodes = {};
25530 var inBetweenNodes = [];
25531 var startAngle, endAngle, totalAngle, eachAngle;
25532 var angle, loc, node, origNode;
25534 if (indexRange < 0) {
25535 indexRange += nodes.length;
25536 } // position this key node
25539 var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
25540 keyPoints[i] = [centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius, centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius];
25541 loc = projection.invert(keyPoints[i]);
25542 node = keyNodes[i];
25543 origNode = origNodes[node.id];
25544 node = node.move(geoVecInterp(origNode.loc, loc, t));
25545 graph = graph.replace(node); // figure out the between delta angle we want to match to
25547 startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
25548 endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
25549 totalAngle = endAngle - startAngle; // detects looping around -pi/pi
25551 if (totalAngle * sign > 0) {
25552 totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
25557 eachAngle = totalAngle / (indexRange + numberNewPoints);
25558 } while (Math.abs(eachAngle) > maxAngle); // move existing nodes
25561 for (j = 1; j < indexRange; j++) {
25562 angle = startAngle + j * eachAngle;
25563 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]);
25564 node = nodes[(j + startNodeIndex) % nodes.length];
25565 origNode = origNodes[node.id];
25566 nearNodes[node.id] = angle;
25567 node = node.move(geoVecInterp(origNode.loc, loc, t));
25568 graph = graph.replace(node);
25569 } // add new in between nodes if necessary
25572 for (j = 0; j < numberNewPoints; j++) {
25573 angle = startAngle + (indexRange + j) * eachAngle;
25574 loc = projection.invert([centroid[0] + Math.cos(angle) * radius, centroid[1] + Math.sin(angle) * radius]); // choose a nearnode to use as the original
25576 var min = Infinity;
25578 for (var nodeId in nearNodes) {
25579 var nearAngle = nearNodes[nodeId];
25580 var dist = Math.abs(nearAngle - angle);
25584 origNode = origNodes[nodeId];
25589 loc: geoVecInterp(origNode.loc, loc, t)
25591 graph = graph.replace(node);
25592 nodes.splice(endNodeIndex + j, 0, node);
25593 inBetweenNodes.push(node.id);
25594 } // Check for other ways that share these keyNodes..
25595 // If keyNodes are adjacent in both ways,
25596 // we can add inBetweenNodes to that shared way too..
25599 if (indexRange === 1 && inBetweenNodes.length) {
25600 var startIndex1 = way.nodes.lastIndexOf(startNode.id);
25601 var endIndex1 = way.nodes.lastIndexOf(endNode.id);
25602 var wayDirection1 = endIndex1 - startIndex1;
25604 if (wayDirection1 < -1) {
25608 var parentWays = graph.parentWays(keyNodes[i]);
25610 for (j = 0; j < parentWays.length; j++) {
25611 var sharedWay = parentWays[j];
25612 if (sharedWay === way) continue;
25614 if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
25615 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
25616 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
25617 var wayDirection2 = endIndex2 - startIndex2;
25618 var insertAt = endIndex2;
25620 if (wayDirection2 < -1) {
25624 if (wayDirection1 !== wayDirection2) {
25625 inBetweenNodes.reverse();
25626 insertAt = startIndex2;
25629 for (k = 0; k < inBetweenNodes.length; k++) {
25630 sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
25633 graph = graph.replace(sharedWay);
25637 } // update the way to have all the new nodes
25640 ids = nodes.map(function (n) {
25647 graph = graph.replace(way);
25651 action.makeConvex = function (graph) {
25652 var way = graph.entity(wayId);
25653 var nodes = utilArrayUniq(graph.childNodes(way));
25654 var points = nodes.map(function (n) {
25655 return projection(n.loc);
25657 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
25658 var hull = d3_polygonHull(points);
25659 var i, j; // D3 convex hulls go counterclockwise..
25666 for (i = 0; i < hull.length - 1; i++) {
25667 var startIndex = points.indexOf(hull[i]);
25668 var endIndex = points.indexOf(hull[i + 1]);
25669 var indexRange = endIndex - startIndex;
25671 if (indexRange < 0) {
25672 indexRange += nodes.length;
25673 } // move interior nodes to the surface of the convex hull..
25676 for (j = 1; j < indexRange; j++) {
25677 var point = geoVecInterp(hull[i], hull[i + 1], j / indexRange);
25678 var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
25679 graph = graph.replace(node);
25686 action.disabled = function (graph) {
25687 if (!graph.entity(wayId).isClosed()) {
25688 return 'not_closed';
25689 } //disable when already circular
25692 var way = graph.entity(wayId);
25693 var nodes = utilArrayUniq(graph.childNodes(way));
25694 var points = nodes.map(function (n) {
25695 return projection(n.loc);
25697 var hull = d3_polygonHull(points);
25698 var epsilonAngle = Math.PI / 180;
25700 if (hull.length !== points.length || hull.length < 3) {
25704 var centroid = d3_polygonCentroid(points);
25705 var radius = geoVecLengthSquare(centroid, points[0]);
25706 var i, actualPoint; // compare distances between centroid and points
25708 for (i = 0; i < hull.length; i++) {
25709 actualPoint = hull[i];
25710 var actualDist = geoVecLengthSquare(actualPoint, centroid);
25711 var diff = Math.abs(actualDist - radius); //compare distances with epsilon-error (5%)
25713 if (diff > 0.05 * radius) {
25716 } //check if central angles are smaller than maxAngle
25719 for (i = 0; i < hull.length; i++) {
25720 actualPoint = hull[i];
25721 var nextPoint = hull[(i + 1) % hull.length];
25722 var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
25723 var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
25724 var angle = endAngle - startAngle;
25730 if (angle > Math.PI) {
25731 angle = 2 * Math.PI - angle;
25734 if (angle > maxAngle + epsilonAngle) {
25739 return 'already_circular';
25742 action.transitionable = true;
25746 function actionDeleteWay(wayID) {
25747 function canDeleteNode(node, graph) {
25748 // don't delete nodes still attached to ways or relations
25749 if (graph.parentWays(node).length || graph.parentRelations(node).length) return false;
25750 var geometries = osmNodeGeometriesForTags(node.tags); // don't delete if this node can be a standalone point
25752 if (geometries.point) return false; // delete if this node only be a vertex
25754 if (geometries.vertex) return true; // iD doesn't know if this should be a point or vertex,
25755 // so only delete if there are no interesting tags
25757 return !node.hasInterestingTags();
25760 var action = function action(graph) {
25761 var way = graph.entity(wayID);
25762 graph.parentRelations(way).forEach(function (parent) {
25763 parent = parent.removeMembersWithID(wayID);
25764 graph = graph.replace(parent);
25766 if (parent.isDegenerate()) {
25767 graph = actionDeleteRelation(parent.id)(graph);
25770 new Set(way.nodes).forEach(function (nodeID) {
25771 graph = graph.replace(way.removeNode(nodeID));
25772 var node = graph.entity(nodeID);
25774 if (canDeleteNode(node, graph)) {
25775 graph = graph.remove(node);
25778 return graph.remove(way);
25784 function actionDeleteMultiple(ids) {
25786 way: actionDeleteWay,
25787 node: actionDeleteNode,
25788 relation: actionDeleteRelation
25791 var action = function action(graph) {
25792 ids.forEach(function (id) {
25793 if (graph.hasEntity(id)) {
25794 // It may have been deleted already.
25795 graph = actions[graph.entity(id).type](id)(graph);
25804 function actionDeleteRelation(relationID, allowUntaggedMembers) {
25805 function canDeleteEntity(entity, graph) {
25806 return !graph.parentWays(entity).length && !graph.parentRelations(entity).length && !entity.hasInterestingTags() && !allowUntaggedMembers;
25809 var action = function action(graph) {
25810 var relation = graph.entity(relationID);
25811 graph.parentRelations(relation).forEach(function (parent) {
25812 parent = parent.removeMembersWithID(relationID);
25813 graph = graph.replace(parent);
25815 if (parent.isDegenerate()) {
25816 graph = actionDeleteRelation(parent.id)(graph);
25819 var memberIDs = utilArrayUniq(relation.members.map(function (m) {
25822 memberIDs.forEach(function (memberID) {
25823 graph = graph.replace(relation.removeMembersWithID(memberID));
25824 var entity = graph.entity(memberID);
25826 if (canDeleteEntity(entity, graph)) {
25827 graph = actionDeleteMultiple([memberID])(graph);
25830 return graph.remove(relation);
25836 function actionDeleteNode(nodeId) {
25837 var action = function action(graph) {
25838 var node = graph.entity(nodeId);
25839 graph.parentWays(node).forEach(function (parent) {
25840 parent = parent.removeNode(nodeId);
25841 graph = graph.replace(parent);
25843 if (parent.isDegenerate()) {
25844 graph = actionDeleteWay(parent.id)(graph);
25847 graph.parentRelations(node).forEach(function (parent) {
25848 parent = parent.removeMembersWithID(nodeId);
25849 graph = graph.replace(parent);
25851 if (parent.isDegenerate()) {
25852 graph = actionDeleteRelation(parent.id)(graph);
25855 return graph.remove(node);
25862 // First choose a node to be the survivor, with preference given
25863 // to an existing (not new) node.
25865 // Tags and relation memberships of of non-surviving nodes are merged
25866 // to the survivor.
25868 // This is the inverse of `iD.actionDisconnect`.
25871 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
25872 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
25875 function actionConnect(nodeIDs) {
25876 var action = function action(graph) {
25880 var i, j; // Choose a survivor node, prefer an existing (not new) node - #4974
25882 for (i = 0; i < nodeIDs.length; i++) {
25883 survivor = graph.entity(nodeIDs[i]);
25884 if (survivor.version) break; // found one
25885 } // Replace all non-surviving nodes with the survivor and merge tags.
25888 for (i = 0; i < nodeIDs.length; i++) {
25889 node = graph.entity(nodeIDs[i]);
25890 if (node.id === survivor.id) continue;
25891 parents = graph.parentWays(node);
25893 for (j = 0; j < parents.length; j++) {
25894 graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
25897 parents = graph.parentRelations(node);
25899 for (j = 0; j < parents.length; j++) {
25900 graph = graph.replace(parents[j].replaceMember(node, survivor));
25903 survivor = survivor.mergeTags(node.tags);
25904 graph = actionDeleteNode(node.id)(graph);
25907 graph = graph.replace(survivor); // find and delete any degenerate ways created by connecting adjacent vertices
25909 parents = graph.parentWays(survivor);
25911 for (i = 0; i < parents.length; i++) {
25912 if (parents[i].isDegenerate()) {
25913 graph = actionDeleteWay(parents[i].id)(graph);
25920 action.disabled = function (graph) {
25922 var restrictionIDs = [];
25925 var relations, relation, role;
25926 var i, j, k; // Choose a survivor node, prefer an existing (not new) node - #4974
25928 for (i = 0; i < nodeIDs.length; i++) {
25929 survivor = graph.entity(nodeIDs[i]);
25930 if (survivor.version) break; // found one
25931 } // 1. disable if the nodes being connected have conflicting relation roles
25934 for (i = 0; i < nodeIDs.length; i++) {
25935 node = graph.entity(nodeIDs[i]);
25936 relations = graph.parentRelations(node);
25938 for (j = 0; j < relations.length; j++) {
25939 relation = relations[j];
25940 role = relation.memberById(node.id).role || ''; // if this node is a via node in a restriction, remember for later
25942 if (relation.hasFromViaTo()) {
25943 restrictionIDs.push(relation.id);
25946 if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
25949 seen[relation.id] = role;
25952 } // gather restrictions for parent ways
25955 for (i = 0; i < nodeIDs.length; i++) {
25956 node = graph.entity(nodeIDs[i]);
25957 var parents = graph.parentWays(node);
25959 for (j = 0; j < parents.length; j++) {
25960 var parent = parents[j];
25961 relations = graph.parentRelations(parent);
25963 for (k = 0; k < relations.length; k++) {
25964 relation = relations[k];
25966 if (relation.hasFromViaTo()) {
25967 restrictionIDs.push(relation.id);
25971 } // test restrictions
25974 restrictionIDs = utilArrayUniq(restrictionIDs);
25976 for (i = 0; i < restrictionIDs.length; i++) {
25977 relation = graph.entity(restrictionIDs[i]);
25978 if (!relation.isComplete(graph)) continue;
25979 var memberWays = relation.members.filter(function (m) {
25980 return m.type === 'way';
25981 }).map(function (m) {
25982 return graph.entity(m.id);
25984 memberWays = utilArrayUniq(memberWays);
25985 var f = relation.memberByRole('from');
25986 var t = relation.memberByRole('to');
25987 var isUturn = f.id === t.id; // 2a. disable if connection would damage a restriction
25988 // (a key node is a node at the junction of ways)
25998 for (j = 0; j < relation.members.length; j++) {
25999 collectNodes(relation.members[j], nodes);
26002 nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
26003 nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
26004 var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
26005 nodes.from = nodes.from.filter(filter);
26006 nodes.via = nodes.via.filter(filter);
26007 nodes.to = nodes.to.filter(filter);
26008 var connectFrom = false;
26009 var connectVia = false;
26010 var connectTo = false;
26011 var connectKeyFrom = false;
26012 var connectKeyTo = false;
26014 for (j = 0; j < nodeIDs.length; j++) {
26015 var n = nodeIDs[j];
26017 if (nodes.from.indexOf(n) !== -1) {
26018 connectFrom = true;
26021 if (nodes.via.indexOf(n) !== -1) {
26025 if (nodes.to.indexOf(n) !== -1) {
26029 if (nodes.keyfrom.indexOf(n) !== -1) {
26030 connectKeyFrom = true;
26033 if (nodes.keyto.indexOf(n) !== -1) {
26034 connectKeyTo = true;
26038 if (connectFrom && connectTo && !isUturn) {
26039 return 'restriction';
26042 if (connectFrom && connectVia) {
26043 return 'restriction';
26046 if (connectTo && connectVia) {
26047 return 'restriction';
26048 } // connecting to a key node -
26049 // if both nodes are on a member way (i.e. part of the turn restriction),
26050 // the connecting node must be adjacent to the key node.
26053 if (connectKeyFrom || connectKeyTo) {
26054 if (nodeIDs.length !== 2) {
26055 return 'restriction';
26061 for (j = 0; j < memberWays.length; j++) {
26062 way = memberWays[j];
26064 if (way.contains(nodeIDs[0])) {
26068 if (way.contains(nodeIDs[1])) {
26074 // both nodes are part of the restriction
26077 for (j = 0; j < memberWays.length; j++) {
26078 way = memberWays[j];
26080 if (way.areAdjacent(n0, n1)) {
26087 return 'restriction';
26090 } // 2b. disable if nodes being connected will destroy a member way in a restriction
26091 // (to test, make a copy and try actually connecting the nodes)
26094 for (j = 0; j < memberWays.length; j++) {
26095 way = memberWays[j].update({}); // make copy
26097 for (k = 0; k < nodeIDs.length; k++) {
26098 if (nodeIDs[k] === survivor.id) continue;
26100 if (way.areAdjacent(nodeIDs[k], survivor.id)) {
26101 way = way.removeNode(nodeIDs[k]);
26103 way = way.replaceNode(nodeIDs[k], survivor.id);
26107 if (way.isDegenerate()) {
26108 return 'restriction';
26113 return false; // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
26115 function hasDuplicates(n, i, arr) {
26116 return arr.indexOf(n) !== arr.lastIndexOf(n);
26119 function keyNodeFilter(froms, tos) {
26120 return function (n) {
26121 return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
26125 function collectNodes(member, collection) {
26126 var entity = graph.hasEntity(member.id);
26127 if (!entity) return;
26128 var role = member.role || '';
26130 if (!collection[role]) {
26131 collection[role] = [];
26134 if (member.type === 'node') {
26135 collection[role].push(member.id);
26137 if (role === 'via') {
26138 collection.keyfrom.push(member.id);
26139 collection.keyto.push(member.id);
26141 } else if (member.type === 'way') {
26142 collection[role].push.apply(collection[role], entity.nodes);
26144 if (role === 'from' || role === 'via') {
26145 collection.keyfrom.push(entity.first());
26146 collection.keyfrom.push(entity.last());
26149 if (role === 'to' || role === 'via') {
26150 collection.keyto.push(entity.first());
26151 collection.keyto.push(entity.last());
26160 function actionCopyEntities(ids, fromGraph) {
26163 var action = function action(graph) {
26164 ids.forEach(function (id) {
26165 fromGraph.entity(id).copy(fromGraph, _copies);
26168 for (var id in _copies) {
26169 graph = graph.replace(_copies[id]);
26175 action.copies = function () {
26182 function actionDeleteMember(relationId, memberIndex) {
26183 return function (graph) {
26184 var relation = graph.entity(relationId).removeMember(memberIndex);
26185 graph = graph.replace(relation);
26186 if (relation.isDegenerate()) graph = actionDeleteRelation(relation.id)(graph);
26191 function actionDiscardTags(difference, discardTags) {
26192 discardTags = discardTags || {};
26193 return function (graph) {
26194 difference.modified().forEach(checkTags);
26195 difference.created().forEach(checkTags);
26198 function checkTags(entity) {
26199 var keys = Object.keys(entity.tags);
26200 var didDiscard = false;
26203 for (var i = 0; i < keys.length; i++) {
26206 if (discardTags[k] || !entity.tags[k]) {
26209 tags[k] = entity.tags[k];
26214 graph = graph.replace(entity.update({
26223 // Optionally, disconnect only the given ways.
26225 // For testing convenience, accepts an ID to assign to the (first) new node.
26226 // Normally, this will be undefined and the way will automatically
26227 // be assigned a new ID.
26229 // This is the inverse of `iD.actionConnect`.
26232 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
26233 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
26236 function actionDisconnect(nodeId, newNodeId) {
26239 var action = function action(graph) {
26240 var node = graph.entity(nodeId);
26241 var connections = action.connections(graph);
26242 connections.forEach(function (connection) {
26243 var way = graph.entity(connection.wayID);
26244 var newNode = osmNode({
26249 graph = graph.replace(newNode);
26251 if (connection.index === 0 && way.isArea()) {
26252 // replace shared node with shared node..
26253 graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
26254 } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
26255 // replace closing node with new new node..
26256 graph = graph.replace(way.unclose().addNode(newNode.id));
26258 // replace shared node with multiple new nodes..
26259 graph = graph.replace(way.updateNode(newNode.id, connection.index));
26265 action.connections = function (graph) {
26266 var candidates = [];
26267 var keeping = false;
26268 var parentWays = graph.parentWays(graph.entity(nodeId));
26271 for (var i = 0; i < parentWays.length; i++) {
26272 way = parentWays[i];
26274 if (wayIds && wayIds.indexOf(way.id) === -1) {
26279 if (way.isArea() && way.nodes[0] === nodeId) {
26285 for (var j = 0; j < way.nodes.length; j++) {
26286 waynode = way.nodes[j];
26288 if (waynode === nodeId) {
26289 if (way.isClosed() && parentWays.length > 1 && wayIds && wayIds.indexOf(way.id) !== -1 && j === way.nodes.length - 1) {
26302 return keeping ? candidates : candidates.slice(1);
26305 action.disabled = function (graph) {
26306 var connections = action.connections(graph);
26307 if (connections.length === 0) return 'not_connected';
26308 var parentWays = graph.parentWays(graph.entity(nodeId));
26309 var seenRelationIds = {};
26310 var sharedRelation;
26311 parentWays.forEach(function (way) {
26312 var relations = graph.parentRelations(way);
26313 relations.forEach(function (relation) {
26314 if (relation.id in seenRelationIds) {
26316 if (wayIds.indexOf(way.id) !== -1 || wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
26317 sharedRelation = relation;
26320 sharedRelation = relation;
26323 seenRelationIds[relation.id] = way.id;
26327 if (sharedRelation) return 'relation';
26330 action.limitWays = function (val) {
26331 if (!arguments.length) return wayIds;
26339 function actionExtract(entityID) {
26340 var extractedNodeID;
26342 var action = function action(graph) {
26343 var entity = graph.entity(entityID);
26345 if (entity.type === 'node') {
26346 return extractFromNode(entity, graph);
26349 return extractFromWayOrRelation(entity, graph);
26352 function extractFromNode(node, graph) {
26353 extractedNodeID = node.id; // Create a new node to replace the one we will detach
26355 var replacement = osmNode({
26358 graph = graph.replace(replacement); // Process each way in turn, updating the graph as we go
26360 graph = graph.parentWays(node).reduce(function (accGraph, parentWay) {
26361 return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
26362 }, graph); // Process any relations too
26364 return graph.parentRelations(node).reduce(function (accGraph, parentRel) {
26365 return accGraph.replace(parentRel.replaceMember(node, replacement));
26369 function extractFromWayOrRelation(entity, graph) {
26370 var fromGeometry = entity.geometry(graph);
26371 var keysToCopyAndRetain = ['source', 'wheelchair'];
26372 var keysToRetain = ['area'];
26373 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
26374 var extractedLoc = d3_geoCentroid(entity.asGeoJSON(graph));
26376 if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
26377 extractedLoc = entity.extent(graph).center();
26380 var indoorAreaValues = {
26387 var isBuilding = entity.tags.building && entity.tags.building !== 'no' || entity.tags['building:part'] && entity.tags['building:part'] !== 'no';
26388 var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
26389 var entityTags = Object.assign({}, entity.tags); // shallow copy
26391 var pointTags = {};
26393 for (var key in entityTags) {
26394 if (entity.type === 'relation' && key === 'type') {
26398 if (keysToRetain.indexOf(key) !== -1) {
26403 // don't transfer building-related tags
26404 if (buildingKeysToRetain.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
26405 } // leave `indoor` tag on the area
26408 if (isIndoorArea && key === 'indoor') {
26410 } // copy the tag from the entity to the point
26413 pointTags[key] = entityTags[key]; // leave addresses and some other tags so they're on both features
26415 if (keysToCopyAndRetain.indexOf(key) !== -1 || key.match(/^addr:.{1,}/)) {
26417 } else if (isIndoorArea && key === 'level') {
26418 // leave `level` on both features
26420 } // remove the tag from the entity
26423 delete entityTags[key];
26426 if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
26427 // ensure that areas keep area geometry
26428 entityTags.area = 'yes';
26431 var replacement = osmNode({
26435 graph = graph.replace(replacement);
26436 extractedNodeID = replacement.id;
26437 return graph.replace(entity.update({
26442 action.getExtractedNodeID = function () {
26443 return extractedNodeID;
26450 // This is the inverse of `iD.actionSplit`.
26453 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
26454 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
26457 function actionJoin(ids) {
26458 function groupEntitiesByGeometry(graph) {
26459 var entities = ids.map(function (id) {
26460 return graph.entity(id);
26462 return Object.assign({
26464 }, utilArrayGroupBy(entities, function (entity) {
26465 return entity.geometry(graph);
26469 var action = function action(graph) {
26470 var ways = ids.map(graph.entity, graph);
26471 var survivorID = ways[0].id; // if any of the ways are sided (e.g. coastline, cliff, kerb)
26472 // sort them first so they establish the overall order - #6033
26474 ways.sort(function (a, b) {
26475 var aSided = a.isSided();
26476 var bSided = b.isSided();
26477 return aSided && !bSided ? -1 : bSided && !aSided ? 1 : 0;
26478 }); // Prefer to keep an existing way.
26480 for (var i = 0; i < ways.length; i++) {
26481 if (!ways[i].isNew()) {
26482 survivorID = ways[i].id;
26487 var sequences = osmJoinWays(ways, graph);
26488 var joined = sequences[0]; // We might need to reverse some of these ways before joining them. #4688
26489 // `joined.actions` property will contain any actions we need to apply.
26491 graph = sequences.actions.reduce(function (g, action) {
26494 var survivor = graph.entity(survivorID);
26495 survivor = survivor.update({
26496 nodes: joined.nodes.map(function (n) {
26500 graph = graph.replace(survivor);
26501 joined.forEach(function (way) {
26502 if (way.id === survivorID) return;
26503 graph.parentRelations(way).forEach(function (parent) {
26504 graph = graph.replace(parent.replaceMember(way, survivor));
26506 survivor = survivor.mergeTags(way.tags);
26507 graph = graph.replace(survivor);
26508 graph = actionDeleteWay(way.id)(graph);
26509 }); // Finds if the join created a single-member multipolygon,
26510 // and if so turns it into a basic area instead
26512 function checkForSimpleMultipolygon() {
26513 if (!survivor.isClosed()) return;
26514 var multipolygons = graph.parentMultipolygons(survivor).filter(function (multipolygon) {
26515 // find multipolygons where the survivor is the only member
26516 return multipolygon.members.length === 1;
26517 }); // skip if this is the single member of multiple multipolygons
26519 if (multipolygons.length !== 1) return;
26520 var multipolygon = multipolygons[0];
26522 for (var key in survivor.tags) {
26523 if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged
26524 multipolygon.tags[key] !== survivor.tags[key]) return;
26527 survivor = survivor.mergeTags(multipolygon.tags);
26528 graph = graph.replace(survivor);
26529 graph = actionDeleteRelation(multipolygon.id, true
26530 /* allow untagged members */
26532 var tags = Object.assign({}, survivor.tags);
26534 if (survivor.geometry(graph) !== 'area') {
26535 // ensure the feature persists as an area
26539 delete tags.type; // remove type=multipolygon
26541 survivor = survivor.update({
26544 graph = graph.replace(survivor);
26547 checkForSimpleMultipolygon();
26549 }; // Returns the number of nodes the resultant way is expected to have
26552 action.resultingWayNodesLength = function (graph) {
26553 return ids.reduce(function (count, id) {
26554 return count + graph.entity(id).nodes.length;
26555 }, 0) - ids.length - 1;
26558 action.disabled = function (graph) {
26559 var geometries = groupEntitiesByGeometry(graph);
26561 if (ids.length < 2 || ids.length !== geometries.line.length) {
26562 return 'not_eligible';
26565 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
26567 if (joined.length > 1) {
26568 return 'not_adjacent';
26569 } // Loop through all combinations of path-pairs
26570 // to check potential intersections between all pairs
26573 for (var i = 0; i < ids.length - 1; i++) {
26574 for (var j = i + 1; j < ids.length; j++) {
26575 var path1 = graph.childNodes(graph.entity(ids[i])).map(function (e) {
26578 var path2 = graph.childNodes(graph.entity(ids[j])).map(function (e) {
26581 var intersections = geoPathIntersections(path1, path2); // Check if intersections are just nodes lying on top of
26582 // each other/the line, as opposed to crossing it
26584 var common = utilArrayIntersection(joined[0].nodes.map(function (n) {
26585 return n.loc.toString();
26586 }), intersections.map(function (n) {
26587 return n.toString();
26590 if (common.length !== intersections.length) {
26591 return 'paths_intersect';
26596 var nodeIds = joined[0].nodes.map(function (n) {
26601 var conflicting = false;
26602 joined[0].forEach(function (way) {
26603 var parents = graph.parentRelations(way);
26604 parents.forEach(function (parent) {
26605 if (parent.isRestriction() && parent.members.some(function (m) {
26606 return nodeIds.indexOf(m.id) >= 0;
26612 for (var k in way.tags) {
26613 if (!(k in tags)) {
26614 tags[k] = way.tags[k];
26615 } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
26616 conflicting = true;
26622 return 'restriction';
26626 return 'conflicting_tags';
26633 function actionMerge(ids) {
26634 function groupEntitiesByGeometry(graph) {
26635 var entities = ids.map(function (id) {
26636 return graph.entity(id);
26638 return Object.assign({
26643 }, utilArrayGroupBy(entities, function (entity) {
26644 return entity.geometry(graph);
26648 var action = function action(graph) {
26649 var geometries = groupEntitiesByGeometry(graph);
26650 var target = geometries.area[0] || geometries.line[0];
26651 var points = geometries.point;
26652 points.forEach(function (point) {
26653 target = target.mergeTags(point.tags);
26654 graph = graph.replace(target);
26655 graph.parentRelations(point).forEach(function (parent) {
26656 graph = graph.replace(parent.replaceMember(point, target));
26658 var nodes = utilArrayUniq(graph.childNodes(target));
26659 var removeNode = point;
26661 for (var i = 0; i < nodes.length; i++) {
26662 var node = nodes[i];
26664 if (graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags()) {
26666 } // Found an uninteresting child node on the target way.
26667 // Move orig point into its place to preserve point's history. #3683
26670 graph = graph.replace(point.update({
26674 target = target.replaceNode(node.id, point.id);
26675 graph = graph.replace(target);
26680 graph = graph.remove(removeNode);
26683 if (target.tags.area === 'yes') {
26684 var tags = Object.assign({}, target.tags); // shallow copy
26688 if (osmTagSuggestingArea(tags)) {
26689 // remove the `area` tag if area geometry is now implied - #3851
26690 target = target.update({
26693 graph = graph.replace(target);
26700 action.disabled = function (graph) {
26701 var geometries = groupEntitiesByGeometry(graph);
26703 if (geometries.point.length === 0 || geometries.area.length + geometries.line.length !== 1 || geometries.relation.length !== 0) {
26704 return 'not_eligible';
26712 // 1. move all the nodes to a common location
26713 // 2. `actionConnect` them
26715 function actionMergeNodes(nodeIDs, loc) {
26716 // If there is a single "interesting" node, use that as the location.
26717 // Otherwise return the average location of all the nodes.
26718 function chooseLoc(graph) {
26719 if (!nodeIDs.length) return null;
26721 var interestingCount = 0;
26722 var interestingLoc;
26724 for (var i = 0; i < nodeIDs.length; i++) {
26725 var node = graph.entity(nodeIDs[i]);
26727 if (node.hasInterestingTags()) {
26728 interestingLoc = ++interestingCount === 1 ? node.loc : null;
26731 sum = geoVecAdd(sum, node.loc);
26734 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
26737 var action = function action(graph) {
26738 if (nodeIDs.length < 2) return graph;
26742 toLoc = chooseLoc(graph);
26745 for (var i = 0; i < nodeIDs.length; i++) {
26746 var node = graph.entity(nodeIDs[i]);
26748 if (node.loc !== toLoc) {
26749 graph = graph.replace(node.move(toLoc));
26753 return actionConnect(nodeIDs)(graph);
26756 action.disabled = function (graph) {
26757 if (nodeIDs.length < 2) return 'not_eligible';
26759 for (var i = 0; i < nodeIDs.length; i++) {
26760 var entity = graph.entity(nodeIDs[i]);
26761 if (entity.type !== 'node') return 'not_eligible';
26764 return actionConnect(nodeIDs).disabled(graph);
26770 function osmChangeset() {
26771 if (!(this instanceof osmChangeset)) {
26772 return new osmChangeset().initialize(arguments);
26773 } else if (arguments.length) {
26774 this.initialize(arguments);
26777 osmEntity.changeset = osmChangeset;
26778 osmChangeset.prototype = Object.create(osmEntity.prototype);
26779 Object.assign(osmChangeset.prototype, {
26781 extent: function extent() {
26782 return new geoExtent();
26784 geometry: function geometry() {
26785 return 'changeset';
26787 asJXON: function asJXON() {
26791 tag: Object.keys(this.tags).map(function (k) {
26803 // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26804 // XML. Returns a string.
26805 osmChangeJXON: function osmChangeJXON(changes) {
26806 var changeset_id = this.id;
26808 function nest(x, order) {
26811 for (var i = 0; i < x.length; i++) {
26812 var tagName = Object.keys(x[i])[0];
26813 if (!groups[tagName]) groups[tagName] = [];
26814 groups[tagName].push(x[i][tagName]);
26818 order.forEach(function (o) {
26819 if (groups[o]) ordered[o] = groups[o];
26822 } // sort relations in a changeset by dependencies
26825 function sort(changes) {
26826 // find a referenced relation in the current changeset
26827 function resolve(item) {
26828 return relations.find(function (relation) {
26829 return item.keyAttributes.type === 'relation' && item.keyAttributes.ref === relation['@id'];
26831 } // a new item is an item that has not been already processed
26834 function isNew(item) {
26835 return !sorted[item['@id']] && !processing.find(function (proc) {
26836 return proc['@id'] === item['@id'];
26840 var processing = [];
26842 var relations = changes.relation;
26843 if (!relations) return changes;
26845 for (var i = 0; i < relations.length; i++) {
26846 var relation = relations[i]; // skip relation if already sorted
26848 if (!sorted[relation['@id']]) {
26849 processing.push(relation);
26852 while (processing.length > 0) {
26853 var next = processing[0],
26854 deps = next.member.map(resolve).filter(Boolean).filter(isNew);
26856 if (deps.length === 0) {
26857 sorted[next['@id']] = next;
26858 processing.shift();
26860 processing = deps.concat(processing);
26865 changes.relation = Object.values(sorted);
26869 function rep(entity) {
26870 return entity.asJXON(changeset_id);
26876 '@generator': 'iD',
26877 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
26878 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26879 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {
26885 asGeoJSON: function asGeoJSON() {
26890 function osmNote() {
26891 if (!(this instanceof osmNote)) {
26892 return new osmNote().initialize(arguments);
26893 } else if (arguments.length) {
26894 this.initialize(arguments);
26898 osmNote.id = function () {
26899 return osmNote.id.next--;
26902 osmNote.id.next = -1;
26903 Object.assign(osmNote.prototype, {
26905 initialize: function initialize(sources) {
26906 for (var i = 0; i < sources.length; ++i) {
26907 var source = sources[i];
26909 for (var prop in source) {
26910 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26911 if (source[prop] === undefined) {
26914 this[prop] = source[prop];
26921 this.id = osmNote.id().toString();
26926 extent: function extent() {
26927 return new geoExtent(this.loc);
26929 update: function update(attrs) {
26930 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
26932 isNew: function isNew() {
26933 return this.id < 0;
26935 move: function move(loc) {
26936 return this.update({
26942 function osmRelation() {
26943 if (!(this instanceof osmRelation)) {
26944 return new osmRelation().initialize(arguments);
26945 } else if (arguments.length) {
26946 this.initialize(arguments);
26949 osmEntity.relation = osmRelation;
26950 osmRelation.prototype = Object.create(osmEntity.prototype);
26952 osmRelation.creationOrder = function (a, b) {
26953 var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
26954 var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
26955 if (aId < 0 || bId < 0) return aId - bId;
26959 Object.assign(osmRelation.prototype, {
26962 copy: function copy(resolver, copies) {
26963 if (copies[this.id]) return copies[this.id];
26964 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
26965 var members = this.members.map(function (member) {
26966 return Object.assign({}, member, {
26967 id: resolver.entity(member.id).copy(resolver, copies).id
26970 copy = copy.update({
26973 copies[this.id] = copy;
26976 extent: function extent(resolver, memo) {
26977 return resolver["transient"](this, 'extent', function () {
26978 if (memo && memo[this.id]) return geoExtent();
26980 memo[this.id] = true;
26981 var extent = geoExtent();
26983 for (var i = 0; i < this.members.length; i++) {
26984 var member = resolver.hasEntity(this.members[i].id);
26987 extent._extend(member.extent(resolver, memo));
26994 geometry: function geometry(graph) {
26995 return graph["transient"](this, 'geometry', function () {
26996 return this.isMultipolygon() ? 'area' : 'relation';
26999 isDegenerate: function isDegenerate() {
27000 return this.members.length === 0;
27002 // Return an array of members, each extended with an 'index' property whose value
27003 // is the member index.
27004 indexedMembers: function indexedMembers() {
27005 var result = new Array(this.members.length);
27007 for (var i = 0; i < this.members.length; i++) {
27008 result[i] = Object.assign({}, this.members[i], {
27015 // Return the first member with the given role. A copy of the member object
27016 // is returned, extended with an 'index' property whose value is the member index.
27017 memberByRole: function memberByRole(role) {
27018 for (var i = 0; i < this.members.length; i++) {
27019 if (this.members[i].role === role) {
27020 return Object.assign({}, this.members[i], {
27026 // Same as memberByRole, but returns all members with the given role
27027 membersByRole: function membersByRole(role) {
27030 for (var i = 0; i < this.members.length; i++) {
27031 if (this.members[i].role === role) {
27032 result.push(Object.assign({}, this.members[i], {
27040 // Return the first member with the given id. A copy of the member object
27041 // is returned, extended with an 'index' property whose value is the member index.
27042 memberById: function memberById(id) {
27043 for (var i = 0; i < this.members.length; i++) {
27044 if (this.members[i].id === id) {
27045 return Object.assign({}, this.members[i], {
27051 // Return the first member with the given id and role. A copy of the member object
27052 // is returned, extended with an 'index' property whose value is the member index.
27053 memberByIdAndRole: function memberByIdAndRole(id, role) {
27054 for (var i = 0; i < this.members.length; i++) {
27055 if (this.members[i].id === id && this.members[i].role === role) {
27056 return Object.assign({}, this.members[i], {
27062 addMember: function addMember(member, index) {
27063 var members = this.members.slice();
27064 members.splice(index === undefined ? members.length : index, 0, member);
27065 return this.update({
27069 updateMember: function updateMember(member, index) {
27070 var members = this.members.slice();
27071 members.splice(index, 1, Object.assign({}, members[index], member));
27072 return this.update({
27076 removeMember: function removeMember(index) {
27077 var members = this.members.slice();
27078 members.splice(index, 1);
27079 return this.update({
27083 removeMembersWithID: function removeMembersWithID(id) {
27084 var members = this.members.filter(function (m) {
27085 return m.id !== id;
27087 return this.update({
27091 moveMember: function moveMember(fromIndex, toIndex) {
27092 var members = this.members.slice();
27093 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
27094 return this.update({
27098 // Wherever a member appears with id `needle.id`, replace it with a member
27099 // with id `replacement.id`, type `replacement.type`, and the original role,
27100 // By default, adding a duplicate member (by id and role) is prevented.
27101 // Return an updated relation.
27102 replaceMember: function replaceMember(needle, replacement, keepDuplicates) {
27103 if (!this.memberById(needle.id)) return this;
27106 for (var i = 0; i < this.members.length; i++) {
27107 var member = this.members[i];
27109 if (member.id !== needle.id) {
27110 members.push(member);
27111 } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
27113 id: replacement.id,
27114 type: replacement.type,
27120 return this.update({
27124 asJXON: function asJXON(changeset_id) {
27127 '@id': this.osmId(),
27128 '@version': this.version || 0,
27129 member: this.members.map(function (member) {
27134 ref: osmEntity.id.toOSM(member.id)
27138 tag: Object.keys(this.tags).map(function (k) {
27149 if (changeset_id) {
27150 r.relation['@changeset'] = changeset_id;
27155 asGeoJSON: function asGeoJSON(resolver) {
27156 return resolver["transient"](this, 'GeoJSON', function () {
27157 if (this.isMultipolygon()) {
27159 type: 'MultiPolygon',
27160 coordinates: this.multipolygon(resolver)
27164 type: 'FeatureCollection',
27165 properties: this.tags,
27166 features: this.members.map(function (member) {
27167 return Object.assign({
27169 }, resolver.entity(member.id).asGeoJSON(resolver));
27175 area: function area(resolver) {
27176 return resolver["transient"](this, 'area', function () {
27177 return d3_geoArea(this.asGeoJSON(resolver));
27180 isMultipolygon: function isMultipolygon() {
27181 return this.tags.type === 'multipolygon';
27183 isComplete: function isComplete(resolver) {
27184 for (var i = 0; i < this.members.length; i++) {
27185 if (!resolver.hasEntity(this.members[i].id)) {
27192 hasFromViaTo: function hasFromViaTo() {
27193 return this.members.some(function (m) {
27194 return m.role === 'from';
27195 }) && this.members.some(function (m) {
27196 return m.role === 'via';
27197 }) && this.members.some(function (m) {
27198 return m.role === 'to';
27201 isRestriction: function isRestriction() {
27202 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27204 isValidRestriction: function isValidRestriction() {
27205 if (!this.isRestriction()) return false;
27206 var froms = this.members.filter(function (m) {
27207 return m.role === 'from';
27209 var vias = this.members.filter(function (m) {
27210 return m.role === 'via';
27212 var tos = this.members.filter(function (m) {
27213 return m.role === 'to';
27215 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
27216 if (froms.some(function (m) {
27217 return m.type !== 'way';
27219 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
27220 if (tos.some(function (m) {
27221 return m.type !== 'way';
27223 if (vias.length === 0) return false;
27224 if (vias.length > 1 && vias.some(function (m) {
27225 return m.type !== 'way';
27229 // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27230 // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27232 // This corresponds to the structure needed for rendering a multipolygon path using a
27233 // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27235 // In the case of invalid geometries, this function will still return a result which
27236 // includes the nodes of all way members, but some Nds may be unclosed and some inner
27237 // rings not matched with the intended outer ring.
27239 multipolygon: function multipolygon(resolver) {
27240 var outers = this.members.filter(function (m) {
27241 return 'outer' === (m.role || 'outer');
27243 var inners = this.members.filter(function (m) {
27244 return 'inner' === m.role;
27246 outers = osmJoinWays(outers, resolver);
27247 inners = osmJoinWays(inners, resolver);
27249 var sequenceToLineString = function sequenceToLineString(sequence) {
27250 if (sequence.nodes.length > 2 && sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
27251 // close unclosed parts to ensure correct area rendering - #2945
27252 sequence.nodes.push(sequence.nodes[0]);
27255 return sequence.nodes.map(function (node) {
27260 outers = outers.map(sequenceToLineString);
27261 inners = inners.map(sequenceToLineString);
27262 var result = outers.map(function (o) {
27263 // Heuristic for detecting counterclockwise winding order. Assumes
27264 // that OpenStreetMap polygons are not hemisphere-spanning.
27265 return [d3_geoArea({
27268 }) > 2 * Math.PI ? o.reverse() : o];
27271 function findOuter(inner) {
27274 for (o = 0; o < outers.length; o++) {
27276 if (geoPolygonContainsPolygon(outer, inner)) return o;
27279 for (o = 0; o < outers.length; o++) {
27281 if (geoPolygonIntersectsPolygon(outer, inner, false)) return o;
27285 for (var i = 0; i < inners.length; i++) {
27286 var inner = inners[i];
27290 coordinates: [inner]
27291 }) < 2 * Math.PI) {
27292 inner = inner.reverse();
27295 var o = findOuter(inners[i]);
27297 if (o !== undefined) {
27298 result[o].push(inners[i]);
27300 result.push([inners[i]]); // Invalid geometry
27308 var QAItem = /*#__PURE__*/function () {
27309 function QAItem(loc, service, itemType, id, props) {
27310 _classCallCheck(this, QAItem);
27312 // Store required properties
27314 this.service = service.title;
27315 this.itemType = itemType; // All issues must have an ID for selection, use generic if none specified
27317 this.id = id ? id : "".concat(QAItem.id());
27318 this.update(props); // Some QA services have marker icons to differentiate issues
27320 if (service && typeof service.getIcon === 'function') {
27321 this.icon = service.getIcon(itemType);
27325 _createClass(QAItem, [{
27327 value: function update(props) {
27330 // You can't override this initial information
27331 var loc = this.loc,
27332 service = this.service,
27333 itemType = this.itemType,
27335 Object.keys(props).forEach(function (prop) {
27336 return _this[prop] = props[prop];
27339 this.service = service;
27340 this.itemType = itemType;
27343 } // Generic handling for newly created QAItems
27347 value: function id() {
27348 return this.nextId--;
27354 QAItem.nextId = -1;
27357 // Optionally, split only the given ways, if multiple ways share
27360 // This is the inverse of `iD.actionJoin`.
27362 // For testing convenience, accepts an ID to assign to the new way.
27363 // Normally, this will be undefined and the way will automatically
27364 // be assigned a new ID.
27367 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
27370 function actionSplit(nodeIds, newWayIds) {
27371 // accept single ID for backwards-compatiblity
27372 if (typeof nodeIds === 'string') nodeIds = [nodeIds];
27374 var _wayIDs; // the strategy for picking which way will have a new version and which way is newly created
27377 var _keepHistoryOn = 'longest'; // 'longest', 'first'
27378 // The IDs of the ways actually created by running this action
27380 var _createdWayIDs = [];
27382 function dist(graph, nA, nB) {
27383 var locA = graph.entity(nA).loc;
27384 var locB = graph.entity(nB).loc;
27385 var epsilon = 1e-6;
27386 return locA && locB ? geoSphericalDistance(locA, locB) : epsilon;
27387 } // If the way is closed, we need to search for a partner node
27388 // to split the way at.
27390 // The following looks for a node that is both far away from
27391 // the initial node in terms of way segment length and nearby
27392 // in terms of beeline-distance. This assures that areas get
27393 // split on the most "natural" points (independent of the number
27395 // For example: bone-shaped areas get split across their waist
27396 // line, circles across the diameter.
27399 function splitArea(nodes, idxA, graph) {
27400 var lengths = new Array(nodes.length);
27406 function wrap(index) {
27407 return utilWrap(index, nodes.length);
27408 } // calculate lengths
27413 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
27414 length += dist(graph, nodes[i], nodes[wrap(i - 1)]);
27415 lengths[i] = length;
27420 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
27421 length += dist(graph, nodes[i], nodes[wrap(i + 1)]);
27423 if (length < lengths[i]) {
27424 lengths[i] = length;
27426 } // determine best opposite node to split
27429 for (i = 0; i < nodes.length; i++) {
27430 var cost = lengths[i] / dist(graph, nodes[idxA], nodes[i]);
27441 function totalLengthBetweenNodes(graph, nodes) {
27442 var totalLength = 0;
27444 for (var i = 0; i < nodes.length - 1; i++) {
27445 totalLength += dist(graph, nodes[i], nodes[i + 1]);
27448 return totalLength;
27451 function split(graph, nodeId, wayA, newWayId) {
27452 var wayB = osmWay({
27455 }); // `wayB` is the NEW way
27457 var origNodes = wayA.nodes.slice();
27460 var isArea = wayA.isArea();
27461 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
27463 if (wayA.isClosed()) {
27464 var nodes = wayA.nodes.slice(0, -1);
27465 var idxA = nodes.indexOf(nodeId);
27466 var idxB = splitArea(nodes, idxA, graph);
27469 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
27470 nodesB = nodes.slice(idxB, idxA + 1);
27472 nodesA = nodes.slice(idxA, idxB + 1);
27473 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
27476 var idx = wayA.nodes.indexOf(nodeId, 1);
27477 nodesA = wayA.nodes.slice(0, idx + 1);
27478 nodesB = wayA.nodes.slice(idx);
27481 var lengthA = totalLengthBetweenNodes(graph, nodesA);
27482 var lengthB = totalLengthBetweenNodes(graph, nodesB);
27484 if (_keepHistoryOn === 'longest' && lengthB > lengthA) {
27485 // keep the history on the longer way, regardless of the node count
27486 wayA = wayA.update({
27489 wayB = wayB.update({
27492 var temp = lengthA;
27496 wayA = wayA.update({
27499 wayB = wayB.update({
27504 if (wayA.tags.step_count) {
27505 // divide up the the step count proportionally between the two ways
27506 var stepCount = parseFloat(wayA.tags.step_count);
27508 if (stepCount && // ensure a number
27509 isFinite(stepCount) && // ensure positive
27510 stepCount > 0 && // ensure integer
27511 Math.round(stepCount) === stepCount) {
27512 var tagsA = Object.assign({}, wayA.tags);
27513 var tagsB = Object.assign({}, wayB.tags);
27514 var ratioA = lengthA / (lengthA + lengthB);
27515 var countA = Math.round(stepCount * ratioA);
27516 tagsA.step_count = countA.toString();
27517 tagsB.step_count = (stepCount - countA).toString();
27518 wayA = wayA.update({
27521 wayB = wayB.update({
27527 graph = graph.replace(wayA);
27528 graph = graph.replace(wayB);
27529 graph.parentRelations(wayA).forEach(function (relation) {
27530 var member; // Turn restrictions - make sure:
27531 // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
27532 // (whichever one is connected to the VIA node/ways)
27533 // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
27535 if (relation.hasFromViaTo()) {
27536 var f = relation.memberByRole('from');
27537 var v = relation.membersByRole('via');
27538 var t = relation.memberByRole('to');
27539 var i; // 1. split a FROM/TO
27541 if (f.id === wayA.id || t.id === wayA.id) {
27544 if (v.length === 1 && v[0].type === 'node') {
27546 keepB = wayB.contains(v[0].id);
27548 // check via way(s)
27549 for (i = 0; i < v.length; i++) {
27550 if (v[i].type === 'way') {
27551 var wayVia = graph.hasEntity(v[i].id);
27553 if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
27562 relation = relation.replaceMember(wayA, wayB);
27563 graph = graph.replace(relation);
27564 } // 2. split a VIA
27567 for (i = 0; i < v.length; i++) {
27568 if (v[i].type === 'way' && v[i].id === wayA.id) {
27574 graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
27578 } // All other relations (Routes, Multipolygons, etc):
27579 // 1. Both `wayA` and `wayB` remain in the relation
27580 // 2. But must be inserted as a pair (see `actionAddMember` for details)
27583 if (relation === isOuter) {
27584 graph = graph.replace(relation.mergeTags(wayA.tags));
27585 graph = graph.replace(wayA.update({
27588 graph = graph.replace(wayB.update({
27596 role: relation.memberById(wayA.id).role
27599 originalID: wayA.id,
27600 insertedID: wayB.id,
27603 graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
27607 if (!isOuter && isArea) {
27608 var multipolygon = osmRelation({
27609 tags: Object.assign({}, wayA.tags, {
27610 type: 'multipolygon'
27622 graph = graph.replace(multipolygon);
27623 graph = graph.replace(wayA.update({
27626 graph = graph.replace(wayB.update({
27631 _createdWayIDs.push(wayB.id);
27636 var action = function action(graph) {
27637 _createdWayIDs = [];
27638 var newWayIndex = 0;
27640 for (var i = 0; i < nodeIds.length; i++) {
27641 var nodeId = nodeIds[i];
27642 var candidates = action.waysForNode(nodeId, graph);
27644 for (var j = 0; j < candidates.length; j++) {
27645 graph = split(graph, nodeId, candidates[j], newWayIds && newWayIds[newWayIndex]);
27653 action.getCreatedWayIDs = function () {
27654 return _createdWayIDs;
27657 action.waysForNode = function (nodeId, graph) {
27658 var node = graph.entity(nodeId);
27659 var splittableParents = graph.parentWays(node).filter(isSplittable);
27662 // If the ways to split aren't specified, only split the lines.
27663 // If there are no lines to split, split the areas.
27664 var hasLine = splittableParents.some(function (parent) {
27665 return parent.geometry(graph) === 'line';
27669 return splittableParents.filter(function (parent) {
27670 return parent.geometry(graph) === 'line';
27675 return splittableParents;
27677 function isSplittable(parent) {
27678 // If the ways to split are specified, ignore everything else.
27679 if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) return false; // We can fake splitting closed ways at their endpoints...
27681 if (parent.isClosed()) return true; // otherwise, we can't split nodes at their endpoints.
27683 for (var i = 1; i < parent.nodes.length - 1; i++) {
27684 if (parent.nodes[i] === nodeId) return true;
27691 action.ways = function (graph) {
27692 return utilArrayUniq([].concat.apply([], nodeIds.map(function (nodeId) {
27693 return action.waysForNode(nodeId, graph);
27697 action.disabled = function (graph) {
27698 for (var i = 0; i < nodeIds.length; i++) {
27699 var nodeId = nodeIds[i];
27700 var candidates = action.waysForNode(nodeId, graph);
27702 if (candidates.length === 0 || _wayIDs && _wayIDs.length !== candidates.length) {
27703 return 'not_eligible';
27708 action.limitWays = function (val) {
27709 if (!arguments.length) return _wayIDs;
27714 action.keepHistoryOn = function (val) {
27715 if (!arguments.length) return _keepHistoryOn;
27716 _keepHistoryOn = val;
27723 function coreGraph(other, mutable) {
27724 if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
27726 if (other instanceof coreGraph) {
27727 var base = other.base();
27728 this.entities = Object.assign(Object.create(base.entities), other.entities);
27729 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
27730 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
27732 this.entities = Object.create({});
27733 this._parentWays = Object.create({});
27734 this._parentRels = Object.create({});
27735 this.rebase(other || [], [this]);
27738 this.transients = {};
27739 this._childNodes = {};
27740 this.frozen = !mutable;
27742 coreGraph.prototype = {
27743 hasEntity: function hasEntity(id) {
27744 return this.entities[id];
27746 entity: function entity(id) {
27747 var entity = this.entities[id]; //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
27750 entity = this.entities.__proto__[id]; // eslint-disable-line no-proto
27754 throw new Error('entity ' + id + ' not found');
27759 geometry: function geometry(id) {
27760 return this.entity(id).geometry(this);
27762 "transient": function transient(entity, key, fn) {
27763 var id = entity.id;
27764 var transients = this.transients[id] || (this.transients[id] = {});
27766 if (transients[key] !== undefined) {
27767 return transients[key];
27770 transients[key] = fn.call(entity);
27771 return transients[key];
27773 parentWays: function parentWays(entity) {
27774 var parents = this._parentWays[entity.id];
27778 parents.forEach(function (id) {
27779 result.push(this.entity(id));
27785 isPoi: function isPoi(entity) {
27786 var parents = this._parentWays[entity.id];
27787 return !parents || parents.size === 0;
27789 isShared: function isShared(entity) {
27790 var parents = this._parentWays[entity.id];
27791 return parents && parents.size > 1;
27793 parentRelations: function parentRelations(entity) {
27794 var parents = this._parentRels[entity.id];
27798 parents.forEach(function (id) {
27799 result.push(this.entity(id));
27805 parentMultipolygons: function parentMultipolygons(entity) {
27806 return this.parentRelations(entity).filter(function (relation) {
27807 return relation.isMultipolygon();
27810 childNodes: function childNodes(entity) {
27811 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27812 if (!entity.nodes) return [];
27815 for (var i = 0; i < entity.nodes.length; i++) {
27816 nodes[i] = this.entity(entity.nodes[i]);
27818 this._childNodes[entity.id] = nodes;
27819 return this._childNodes[entity.id];
27821 base: function base() {
27823 'entities': Object.getPrototypeOf(this.entities),
27824 'parentWays': Object.getPrototypeOf(this._parentWays),
27825 'parentRels': Object.getPrototypeOf(this._parentRels)
27828 // Unlike other graph methods, rebase mutates in place. This is because it
27829 // is used only during the history operation that merges newly downloaded
27830 // data into each state. To external consumers, it should appear as if the
27831 // graph always contained the newly downloaded data.
27832 rebase: function rebase(entities, stack, force) {
27833 var base = this.base();
27836 for (i = 0; i < entities.length; i++) {
27837 var entity = entities[i];
27838 if (!entity.visible || !force && base.entities[entity.id]) continue; // Merging data into the base graph
27840 base.entities[entity.id] = entity;
27842 this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); // Restore provisionally-deleted nodes that are discovered to have an extant parent
27845 if (entity.type === 'way') {
27846 for (j = 0; j < entity.nodes.length; j++) {
27847 id = entity.nodes[j];
27849 for (k = 1; k < stack.length; k++) {
27850 var ents = stack[k].entities;
27852 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27860 for (i = 0; i < stack.length; i++) {
27861 stack[i]._updateRebased();
27864 _updateRebased: function _updateRebased() {
27865 var base = this.base();
27866 Object.keys(this._parentWays).forEach(function (child) {
27867 if (base.parentWays[child]) {
27868 base.parentWays[child].forEach(function (id) {
27869 if (!this.entities.hasOwnProperty(id)) {
27870 this._parentWays[child].add(id);
27875 Object.keys(this._parentRels).forEach(function (child) {
27876 if (base.parentRels[child]) {
27877 base.parentRels[child].forEach(function (id) {
27878 if (!this.entities.hasOwnProperty(id)) {
27879 this._parentRels[child].add(id);
27884 this.transients = {}; // this._childNodes is not updated, under the assumption that
27885 // ways are always downloaded with their child nodes.
27887 // Updates calculated properties (parentWays, parentRels) for the specified change
27888 _updateCalculated: function _updateCalculated(oldentity, entity, parentWays, parentRels) {
27889 parentWays = parentWays || this._parentWays;
27890 parentRels = parentRels || this._parentRels;
27891 var type = entity && entity.type || oldentity && oldentity.type;
27892 var removed, added, i;
27894 if (type === 'way') {
27895 // Update parentWays
27896 if (oldentity && entity) {
27897 removed = utilArrayDifference(oldentity.nodes, entity.nodes);
27898 added = utilArrayDifference(entity.nodes, oldentity.nodes);
27899 } else if (oldentity) {
27900 removed = oldentity.nodes;
27902 } else if (entity) {
27904 added = entity.nodes;
27907 for (i = 0; i < removed.length; i++) {
27908 // make a copy of prototype property, store as own property, and update..
27909 parentWays[removed[i]] = new Set(parentWays[removed[i]]);
27910 parentWays[removed[i]]["delete"](oldentity.id);
27913 for (i = 0; i < added.length; i++) {
27914 // make a copy of prototype property, store as own property, and update..
27915 parentWays[added[i]] = new Set(parentWays[added[i]]);
27916 parentWays[added[i]].add(entity.id);
27918 } else if (type === 'relation') {
27919 // Update parentRels
27920 // diff only on the IDs since the same entity can be a member multiple times with different roles
27921 var oldentityMemberIDs = oldentity ? oldentity.members.map(function (m) {
27924 var entityMemberIDs = entity ? entity.members.map(function (m) {
27928 if (oldentity && entity) {
27929 removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
27930 added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
27931 } else if (oldentity) {
27932 removed = oldentityMemberIDs;
27934 } else if (entity) {
27936 added = entityMemberIDs;
27939 for (i = 0; i < removed.length; i++) {
27940 // make a copy of prototype property, store as own property, and update..
27941 parentRels[removed[i]] = new Set(parentRels[removed[i]]);
27942 parentRels[removed[i]]["delete"](oldentity.id);
27945 for (i = 0; i < added.length; i++) {
27946 // make a copy of prototype property, store as own property, and update..
27947 parentRels[added[i]] = new Set(parentRels[added[i]]);
27948 parentRels[added[i]].add(entity.id);
27952 replace: function replace(entity) {
27953 if (this.entities[entity.id] === entity) return this;
27954 return this.update(function () {
27955 this._updateCalculated(this.entities[entity.id], entity);
27957 this.entities[entity.id] = entity;
27960 remove: function remove(entity) {
27961 return this.update(function () {
27962 this._updateCalculated(entity, undefined);
27964 this.entities[entity.id] = undefined;
27967 revert: function revert(id) {
27968 var baseEntity = this.base().entities[id];
27969 var headEntity = this.entities[id];
27970 if (headEntity === baseEntity) return this;
27971 return this.update(function () {
27972 this._updateCalculated(headEntity, baseEntity);
27974 delete this.entities[id];
27977 update: function update() {
27978 var graph = this.frozen ? coreGraph(this, true) : this;
27980 for (var i = 0; i < arguments.length; i++) {
27981 arguments[i].call(graph, graph);
27984 if (this.frozen) graph.frozen = true;
27987 // Obliterates any existing entities
27988 load: function load(entities) {
27989 var base = this.base();
27990 this.entities = Object.create(base.entities);
27992 for (var i in entities) {
27993 this.entities[i] = entities[i];
27995 this._updateCalculated(base.entities[i], this.entities[i]);
28002 function osmTurn(turn) {
28003 if (!(this instanceof osmTurn)) {
28004 return new osmTurn(turn);
28007 Object.assign(this, turn);
28009 function osmIntersection(graph, startVertexId, maxDistance) {
28010 maxDistance = maxDistance || 30; // in meters
28012 var vgraph = coreGraph(); // virtual graph
28016 function memberOfRestriction(entity) {
28017 return graph.parentRelations(entity).some(function (r) {
28018 return r.isRestriction();
28022 function isRoad(way) {
28023 if (way.isArea() || way.isDegenerate()) return false;
28026 'motorway_link': true,
28028 'trunk_link': true,
28030 'primary_link': true,
28032 'secondary_link': true,
28034 'tertiary_link': true,
28035 'residential': true,
28036 'unclassified': true,
28037 'living_street': true,
28042 return roads[way.tags.highway];
28045 var startNode = graph.entity(startVertexId);
28046 var checkVertices = [startNode];
28049 var vertexIds = [];
28057 var parent; // `actions` will store whatever actions must be performed to satisfy
28058 // preconditions for adding a turn restriction to this intersection.
28059 // - Remove any existing degenerate turn restrictions (missing from/to, etc)
28060 // - Reverse oneways so that they are drawn in the forward direction
28061 // - Split ways on key vertices
28063 var actions = []; // STEP 1: walk the graph outwards from starting vertex to search
28064 // for more key vertices and ways to include in the intersection..
28066 while (checkVertices.length) {
28067 vertex = checkVertices.pop(); // check this vertex for parent ways that are roads
28069 checkWays = graph.parentWays(vertex);
28070 var hasWays = false;
28072 for (i = 0; i < checkWays.length; i++) {
28073 way = checkWays[i];
28074 if (!isRoad(way) && !memberOfRestriction(way)) continue;
28075 ways.push(way); // it's a road, or it's already in a turn restriction
28077 hasWays = true; // check the way's children for more key vertices
28079 nodes = utilArrayUniq(graph.childNodes(way));
28081 for (j = 0; j < nodes.length; j++) {
28083 if (node === vertex) continue; // same thing
28085 if (vertices.indexOf(node) !== -1) continue; // seen it already
28087 if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start
28088 // a key vertex will have parents that are also roads
28090 var hasParents = false;
28091 parents = graph.parentWays(node);
28093 for (k = 0; k < parents.length; k++) {
28094 parent = parents[k];
28095 if (parent === way) continue; // same thing
28097 if (ways.indexOf(parent) !== -1) continue; // seen it already
28099 if (!isRoad(parent)) continue; // not a road
28106 checkVertices.push(node);
28112 vertices.push(vertex);
28116 vertices = utilArrayUniq(vertices);
28117 ways = utilArrayUniq(ways); // STEP 2: Build a virtual graph containing only the entities in the intersection..
28118 // Everything done after this step should act on the virtual graph
28119 // Any actions that must be performed later to the main graph go in `actions` array
28121 ways.forEach(function (way) {
28122 graph.childNodes(way).forEach(function (node) {
28123 vgraph = vgraph.replace(node);
28125 vgraph = vgraph.replace(way);
28126 graph.parentRelations(way).forEach(function (relation) {
28127 if (relation.isRestriction()) {
28128 if (relation.isValidRestriction(graph)) {
28129 vgraph = vgraph.replace(relation);
28130 } else if (relation.isComplete(graph)) {
28131 actions.push(actionDeleteRelation(relation.id));
28135 }); // STEP 3: Force all oneways to be drawn in the forward direction
28137 ways.forEach(function (w) {
28138 var way = vgraph.entity(w.id);
28140 if (way.tags.oneway === '-1') {
28141 var action = actionReverse(way.id, {
28142 reverseOneway: true
28144 actions.push(action);
28145 vgraph = action(vgraph);
28147 }); // STEP 4: Split ways on key vertices
28149 var origCount = osmEntity.id.next.way;
28150 vertices.forEach(function (v) {
28151 // This is an odd way to do it, but we need to find all the ways that
28152 // will be split here, then split them one at a time to ensure that these
28153 // actions can be replayed on the main graph exactly in the same order.
28154 // (It is unintuitive, but the order of ways returned from graph.parentWays()
28155 // is arbitrary, depending on how the main graph and vgraph were built)
28156 var splitAll = actionSplit([v.id]).keepHistoryOn('first');
28158 if (!splitAll.disabled(vgraph)) {
28159 splitAll.ways(vgraph).forEach(function (way) {
28160 var splitOne = actionSplit([v.id]).limitWays([way.id]).keepHistoryOn('first');
28161 actions.push(splitOne);
28162 vgraph = splitOne(vgraph);
28165 }); // In here is where we should also split the intersection at nearby junction.
28166 // for https://github.com/mapbox/iD-internal/issues/31
28167 // nearbyVertices.forEach(function(v) {
28169 // Reasons why we reset the way id count here:
28170 // 1. Continuity with way ids created by the splits so that we can replay
28171 // these actions later if the user decides to create a turn restriction
28172 // 2. Avoids churning way ids just by hovering over a vertex
28173 // and displaying the turn restriction editor
28175 osmEntity.id.next.way = origCount; // STEP 5: Update arrays to point to vgraph entities
28177 vertexIds = vertices.map(function (v) {
28182 vertexIds.forEach(function (id) {
28183 var vertex = vgraph.entity(id);
28184 var parents = vgraph.parentWays(vertex);
28185 vertices.push(vertex);
28186 ways = ways.concat(parents);
28188 vertices = utilArrayUniq(vertices);
28189 ways = utilArrayUniq(ways);
28190 vertexIds = vertices.map(function (v) {
28193 wayIds = ways.map(function (w) {
28195 }); // STEP 6: Update the ways with some metadata that will be useful for
28196 // walking the intersection graph later and rendering turn arrows.
28198 function withMetadata(way, vertexIds) {
28199 var __oneWay = way.isOneWay(); // which affixes are key vertices?
28202 var __first = vertexIds.indexOf(way.first()) !== -1;
28204 var __last = vertexIds.indexOf(way.last()) !== -1; // what roles is this way eligible for?
28207 var __via = __first && __last;
28209 var __from = __first && !__oneWay || __last;
28211 var __to = __first || __last && !__oneWay;
28213 return way.update({
28224 wayIds.forEach(function (id) {
28225 var way = withMetadata(vgraph.entity(id), vertexIds);
28226 vgraph = vgraph.replace(way);
28228 }); // STEP 7: Simplify - This is an iterative process where we:
28229 // 1. Find trivial vertices with only 2 parents
28230 // 2. trim off the leaf way from those vertices and remove from vgraph
28233 var removeWayIds = [];
28234 var removeVertexIds = [];
28238 checkVertices = vertexIds.slice();
28240 for (i = 0; i < checkVertices.length; i++) {
28241 var vertexId = checkVertices[i];
28242 vertex = vgraph.hasEntity(vertexId);
28245 if (vertexIds.indexOf(vertexId) !== -1) {
28246 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28249 removeVertexIds.push(vertexId);
28253 parents = vgraph.parentWays(vertex);
28255 if (parents.length < 3) {
28256 if (vertexIds.indexOf(vertexId) !== -1) {
28257 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28261 if (parents.length === 2) {
28262 // vertex with 2 parents is trivial
28263 var a = parents[0];
28264 var b = parents[1];
28265 var aIsLeaf = a && !a.__via;
28266 var bIsLeaf = b && !b.__via;
28267 var leaf, survivor;
28269 if (aIsLeaf && !bIsLeaf) {
28272 } else if (!aIsLeaf && bIsLeaf) {
28277 if (leaf && survivor) {
28278 survivor = withMetadata(survivor, vertexIds); // update survivor way
28280 vgraph = vgraph.replace(survivor).remove(leaf); // update graph
28282 removeWayIds.push(leaf.id);
28287 parents = vgraph.parentWays(vertex);
28289 if (parents.length < 2) {
28290 // vertex is no longer a key vertex
28291 if (vertexIds.indexOf(vertexId) !== -1) {
28292 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28295 removeVertexIds.push(vertexId);
28299 if (parents.length < 1) {
28300 // vertex is no longer attached to anything
28301 vgraph = vgraph.remove(vertex);
28304 } while (keepGoing);
28306 vertices = vertices.filter(function (vertex) {
28307 return removeVertexIds.indexOf(vertex.id) === -1;
28308 }).map(function (vertex) {
28309 return vgraph.entity(vertex.id);
28311 ways = ways.filter(function (way) {
28312 return removeWayIds.indexOf(way.id) === -1;
28313 }).map(function (way) {
28314 return vgraph.entity(way.id);
28315 }); // OK! Here is our intersection..
28317 var intersection = {
28320 vertices: vertices,
28322 }; // Get all the valid turns through this intersection given a starting way id.
28323 // This operates on the virtual graph for everything.
28325 // Basically, walk through all possible paths from starting way,
28326 // honoring the existing turn restrictions as we go (watch out for loops!)
28328 // For each path found, generate and return a `osmTurn` datastructure.
28331 intersection.turns = function (fromWayId, maxViaWay) {
28332 if (!fromWayId) return [];
28333 if (!maxViaWay) maxViaWay = 0;
28334 var vgraph = intersection.graph;
28335 var keyVertexIds = intersection.vertices.map(function (v) {
28338 var start = vgraph.entity(fromWayId);
28339 if (!start || !(start.__from || start.__via)) return []; // maxViaWay=0 from-*-to (0 vias)
28340 // maxViaWay=1 from-*-via-*-to (1 via max)
28341 // maxViaWay=2 from-*-via-*-via-*-to (2 vias max)
28343 var maxPathLength = maxViaWay * 2 + 3;
28346 return turns; // traverse the intersection graph and find all the valid paths
28348 function step(entity, currPath, currRestrictions, matchedRestriction) {
28349 currPath = (currPath || []).slice(); // shallow copy
28351 if (currPath.length >= maxPathLength) return;
28352 currPath.push(entity.id);
28353 currRestrictions = (currRestrictions || []).slice(); // shallow copy
28357 if (entity.type === 'node') {
28358 var parents = vgraph.parentWays(entity);
28359 var nextWays = []; // which ways can we step into?
28361 for (i = 0; i < parents.length; i++) {
28362 var way = parents[i]; // if next way is a oneway incoming to this vertex, skip
28364 if (way.__oneWay && way.nodes[0] !== entity.id) continue; // if we have seen it before (allowing for an initial u-turn), skip
28366 if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; // Check all "current" restrictions (where we've already walked the `FROM`)
28368 var restrict = null;
28370 for (j = 0; j < currRestrictions.length; j++) {
28371 var restriction = currRestrictions[j];
28372 var f = restriction.memberByRole('from');
28373 var v = restriction.membersByRole('via');
28374 var t = restriction.memberByRole('to');
28375 var isOnly = /^only_/.test(restriction.tags.restriction); // Does the current path match this turn restriction?
28377 var matchesFrom = f.id === fromWayId;
28378 var matchesViaTo = false;
28379 var isAlongOnlyPath = false;
28381 if (t.id === way.id) {
28383 if (v.length === 1 && v[0].type === 'node') {
28385 matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
28387 // match all VIA ways
28390 for (k = 2; k < currPath.length; k += 2) {
28391 // k = 2 skips FROM
28392 pathVias.push(currPath[k]); // (path goes way-node-way...)
28395 var restrictionVias = [];
28397 for (k = 0; k < v.length; k++) {
28398 if (v[k].type === 'way') {
28399 restrictionVias.push(v[k].id);
28403 var diff = utilArrayDifference(pathVias, restrictionVias);
28404 matchesViaTo = !diff.length;
28406 } else if (isOnly) {
28407 for (k = 0; k < v.length; k++) {
28408 // way doesn't match TO, but is one of the via ways along the path of an "only"
28409 if (v[k].type === 'way' && v[k].id === way.id) {
28410 isAlongOnlyPath = true;
28416 if (matchesViaTo) {
28419 id: restriction.id,
28420 direct: matchesFrom,
28427 id: restriction.id,
28428 direct: matchesFrom,
28435 // indirect - caused by a different nearby restriction
28436 if (isAlongOnlyPath) {
28438 id: restriction.id,
28444 } else if (isOnly) {
28446 id: restriction.id,
28453 } // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
28456 if (restrict && restrict.direct) break;
28465 nextWays.forEach(function (nextWay) {
28466 step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
28469 // entity.type === 'way'
28470 if (currPath.length >= 3) {
28471 // this is a "complete" path..
28472 var turnPath = currPath.slice(); // shallow copy
28473 // an indirect restriction - only include the partial path (starting at FROM)
28475 if (matchedRestriction && matchedRestriction.direct === false) {
28476 for (i = 0; i < turnPath.length; i++) {
28477 if (turnPath[i] === matchedRestriction.from) {
28478 turnPath = turnPath.slice(i);
28484 var turn = pathToTurn(turnPath);
28487 if (matchedRestriction) {
28488 turn.restrictionID = matchedRestriction.id;
28489 turn.no = matchedRestriction.no;
28490 turn.only = matchedRestriction.only;
28491 turn.direct = matchedRestriction.direct;
28494 turns.push(osmTurn(turn));
28497 if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here
28500 if (matchedRestriction && matchedRestriction.end) return; // don't advance any further
28501 // which nodes can we step into?
28503 var n1 = vgraph.entity(entity.first());
28504 var n2 = vgraph.entity(entity.last());
28505 var dist = geoSphericalDistance(n1.loc, n2.loc);
28506 var nextNodes = [];
28508 if (currPath.length > 1) {
28509 if (dist > maxDistance) return; // the next node is too far
28511 if (!entity.__via) return; // this way is a leaf / can't be a via
28514 if (!entity.__oneWay && // bidirectional..
28515 keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
28516 currPath.indexOf(n1.id) === -1) {
28517 // haven't seen it yet..
28518 nextNodes.push(n1); // can advance to first node
28521 if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
28522 currPath.indexOf(n2.id) === -1) {
28523 // haven't seen it yet..
28524 nextNodes.push(n2); // can advance to last node
28527 nextNodes.forEach(function (nextNode) {
28528 // gather restrictions FROM this way
28529 var fromRestrictions = vgraph.parentRelations(entity).filter(function (r) {
28530 if (!r.isRestriction()) return false;
28531 var f = r.memberByRole('from');
28532 if (!f || f.id !== entity.id) return false;
28533 var isOnly = /^only_/.test(r.tags.restriction);
28534 if (!isOnly) return true; // `only_` restrictions only matter along the direction of the VIA - #4849
28536 var isOnlyVia = false;
28537 var v = r.membersByRole('via');
28539 if (v.length === 1 && v[0].type === 'node') {
28541 isOnlyVia = v[0].id === nextNode.id;
28544 for (var i = 0; i < v.length; i++) {
28545 if (v[i].type !== 'way') continue;
28546 var viaWay = vgraph.entity(v[i].id);
28548 if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
28557 step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
28560 } // assumes path is alternating way-node-way of odd length
28563 function pathToTurn(path) {
28564 if (path.length < 3) return;
28565 var fromWayId, fromNodeId, fromVertexId;
28566 var toWayId, toNodeId, toVertexId;
28567 var viaWayIds, viaNodeId, isUturn;
28568 fromWayId = path[0];
28569 toWayId = path[path.length - 1];
28571 if (path.length === 3 && fromWayId === toWayId) {
28573 var way = vgraph.entity(fromWayId);
28574 if (way.__oneWay) return null;
28576 viaNodeId = fromVertexId = toVertexId = path[1];
28577 fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
28580 fromVertexId = path[1];
28581 fromNodeId = adjacentNode(fromWayId, fromVertexId);
28582 toVertexId = path[path.length - 2];
28583 toNodeId = adjacentNode(toWayId, toVertexId);
28585 if (path.length === 3) {
28586 viaNodeId = path[1];
28588 viaWayIds = path.filter(function (entityId) {
28589 return entityId[0] === 'w';
28591 viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last
28596 key: path.join('_'),
28601 vertex: fromVertexId
28615 function adjacentNode(wayId, affixId) {
28616 var nodes = vgraph.entity(wayId).nodes;
28617 return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
28622 return intersection;
28624 function osmInferRestriction(graph, turn, projection) {
28625 var fromWay = graph.entity(turn.from.way);
28626 var fromNode = graph.entity(turn.from.node);
28627 var fromVertex = graph.entity(turn.from.vertex);
28628 var toWay = graph.entity(turn.to.way);
28629 var toNode = graph.entity(turn.to.node);
28630 var toVertex = graph.entity(turn.to.vertex);
28631 var fromOneWay = fromWay.tags.oneway === 'yes';
28632 var toOneWay = toWay.tags.oneway === 'yes';
28633 var angle = (geoAngle(fromVertex, fromNode, projection) - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
28635 while (angle < 0) {
28639 if (fromNode === toNode) return 'no_u_turn';
28640 if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway
28642 if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex) return 'no_u_turn'; // even wider tolerance for u-turn if there is a via way (from !== to)
28644 if (angle < 158) return 'no_right_turn';
28645 if (angle > 202) return 'no_left_turn';
28646 return 'no_straight_on';
28649 function actionMergePolygon(ids, newRelationId) {
28650 function groupEntities(graph) {
28651 var entities = ids.map(function (id) {
28652 return graph.entity(id);
28654 var geometryGroups = utilArrayGroupBy(entities, function (entity) {
28655 if (entity.type === 'way' && entity.isClosed()) {
28656 return 'closedWay';
28657 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
28658 return 'multipolygon';
28663 return Object.assign({
28667 }, geometryGroups);
28670 var action = function action(graph) {
28671 var entities = groupEntities(graph); // An array representing all the polygons that are part of the multipolygon.
28673 // Each element is itself an array of objects with an id property, and has a
28674 // locs property which is an array of the locations forming the polygon.
28676 var polygons = entities.multipolygon.reduce(function (polygons, m) {
28677 return polygons.concat(osmJoinWays(m.members, graph));
28678 }, []).concat(entities.closedWay.map(function (d) {
28682 member.nodes = graph.childNodes(d);
28684 })); // contained is an array of arrays of boolean values,
28685 // where contained[j][k] is true iff the jth way is
28686 // contained by the kth way.
28688 var contained = polygons.map(function (w, i) {
28689 return polygons.map(function (d, n) {
28690 if (i === n) return null;
28691 return geoPolygonContainsPolygon(d.nodes.map(function (n) {
28693 }), w.nodes.map(function (n) {
28697 }); // Sort all polygons as either outer or inner ways
28702 while (polygons.length) {
28703 extractUncontained(polygons);
28704 polygons = polygons.filter(isContained);
28705 contained = contained.filter(isContained).map(filterContained);
28708 function isContained(d, i) {
28709 return contained[i].some(function (val) {
28714 function filterContained(d) {
28715 return d.filter(isContained);
28718 function extractUncontained(polygons) {
28719 polygons.forEach(function (d, i) {
28720 if (!isContained(d, i)) {
28721 d.forEach(function (member) {
28725 role: outer ? 'outer' : 'inner'
28731 } // Move all tags to one relation
28734 var relation = entities.multipolygon[0] || osmRelation({
28737 type: 'multipolygon'
28740 entities.multipolygon.slice(1).forEach(function (m) {
28741 relation = relation.mergeTags(m.tags);
28742 graph = graph.remove(m);
28744 entities.closedWay.forEach(function (way) {
28745 function isThisOuter(m) {
28746 return m.id === way.id && m.role !== 'inner';
28749 if (members.some(isThisOuter)) {
28750 relation = relation.mergeTags(way.tags);
28751 graph = graph.replace(way.update({
28756 return graph.replace(relation.update({
28758 tags: utilObjectOmit(relation.tags, ['area'])
28762 action.disabled = function (graph) {
28763 var entities = groupEntities(graph);
28765 if (entities.other.length > 0 || entities.closedWay.length + entities.multipolygon.length < 2) {
28766 return 'not_eligible';
28769 if (!entities.multipolygon.every(function (r) {
28770 return r.isComplete(graph);
28772 return 'incomplete_relation';
28775 if (!entities.multipolygon.length) {
28776 var sharedMultipolygons = [];
28777 entities.closedWay.forEach(function (way, i) {
28779 sharedMultipolygons = graph.parentMultipolygons(way);
28781 sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
28784 sharedMultipolygons = sharedMultipolygons.filter(function (relation) {
28785 return relation.members.length === entities.closedWay.length;
28788 if (sharedMultipolygons.length) {
28789 // don't create a new multipolygon if it'd be redundant
28790 return 'not_eligible';
28792 } else if (entities.closedWay.some(function (way) {
28793 return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
28795 // don't add a way to a multipolygon again if it's already a member
28796 return 'not_eligible';
28803 var UNSUPPORTED_Y$3 = regexpStickyHelpers.UNSUPPORTED_Y;
28805 // `RegExp.prototype.flags` getter
28806 // https://tc39.github.io/ecma262/#sec-get-regexp.prototype.flags
28807 if (descriptors && (/./g.flags != 'g' || UNSUPPORTED_Y$3)) {
28808 objectDefineProperty.f(RegExp.prototype, 'flags', {
28809 configurable: true,
28814 var fastDeepEqual = function equal(a, b) {
28815 if (a === b) return true;
28817 if (a && b && _typeof(a) == 'object' && _typeof(b) == 'object') {
28818 if (a.constructor !== b.constructor) return false;
28819 var length, i, keys;
28821 if (Array.isArray(a)) {
28823 if (length != b.length) return false;
28825 for (i = length; i-- !== 0;) {
28826 if (!equal(a[i], b[i])) return false;
28832 if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
28833 if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
28834 if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
28835 keys = Object.keys(a);
28836 length = keys.length;
28837 if (length !== Object.keys(b).length) return false;
28839 for (i = length; i-- !== 0;) {
28840 if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
28843 for (i = length; i-- !== 0;) {
28845 if (!equal(a[key], b[key])) return false;
28849 } // true if both NaN, false otherwise
28852 return a !== a && b !== b;
28855 // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
28856 // comparison, Bell Telephone Laboratories CSTR #41 (1976)
28857 // http://www.cs.dartmouth.edu/~doug/
28858 // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
28860 // Expects two arrays, finds longest common sequence
28862 function LCS(buffer1, buffer2) {
28863 var equivalenceClasses = {};
28865 for (var j = 0; j < buffer2.length; j++) {
28866 var item = buffer2[j];
28868 if (equivalenceClasses[item]) {
28869 equivalenceClasses[item].push(j);
28871 equivalenceClasses[item] = [j];
28880 var candidates = [NULLRESULT];
28882 for (var i = 0; i < buffer1.length; i++) {
28883 var _item = buffer1[i];
28884 var buffer2indices = equivalenceClasses[_item] || [];
28886 var c = candidates[0];
28888 for (var jx = 0; jx < buffer2indices.length; jx++) {
28889 var _j = buffer2indices[jx];
28892 for (s = r; s < candidates.length; s++) {
28893 if (candidates[s].buffer2index < _j && (s === candidates.length - 1 || candidates[s + 1].buffer2index > _j)) {
28898 if (s < candidates.length) {
28899 var newCandidate = {
28902 chain: candidates[s]
28905 if (r === candidates.length) {
28906 candidates.push(c);
28914 if (r === candidates.length) {
28915 break; // no point in examining further (j)s
28921 } // At this point, we know the LCS: it's in the reverse of the
28922 // linked-list through .chain of candidates[candidates.length - 1].
28925 return candidates[candidates.length - 1];
28926 } // We apply the LCS to build a 'comm'-style picture of the
28927 // offsets and lengths of mismatched chunks in the input
28928 // buffers. This is used by diff3MergeRegions.
28931 function diffIndices(buffer1, buffer2) {
28932 var lcs = LCS(buffer1, buffer2);
28934 var tail1 = buffer1.length;
28935 var tail2 = buffer2.length;
28937 for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
28938 var mismatchLength1 = tail1 - candidate.buffer1index - 1;
28939 var mismatchLength2 = tail2 - candidate.buffer2index - 1;
28940 tail1 = candidate.buffer1index;
28941 tail2 = candidate.buffer2index;
28943 if (mismatchLength1 || mismatchLength2) {
28945 buffer1: [tail1 + 1, mismatchLength1],
28946 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
28947 buffer2: [tail2 + 1, mismatchLength2],
28948 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
28955 } // We apply the LCS to build a JSON representation of a
28956 // independently derived from O, returns a fairly complicated
28957 // internal representation of merge decisions it's taken. The
28958 // interested reader may wish to consult
28960 // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
28961 // 'A Formal Investigation of ' In Arvind and Prasad,
28962 // editors, Foundations of Software Technology and Theoretical
28963 // Computer Science (FSTTCS), December 2007.
28965 // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
28969 function diff3MergeRegions(a, o, b) {
28970 // "hunks" are array subsets where `a` or `b` are different from `o`
28971 // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
28974 function addHunk(h, ab) {
28977 oStart: h.buffer1[0],
28978 oLength: h.buffer1[1],
28979 // length of o to remove
28980 abStart: h.buffer2[0],
28981 abLength: h.buffer2[1] // length of a/b to insert
28982 // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
28987 diffIndices(o, a).forEach(function (item) {
28988 return addHunk(item, 'a');
28990 diffIndices(o, b).forEach(function (item) {
28991 return addHunk(item, 'b');
28993 hunks.sort(function (x, y) {
28994 return x.oStart - y.oStart;
28997 var currOffset = 0;
28999 function advanceTo(endOffset) {
29000 if (endOffset > currOffset) {
29004 bufferStart: currOffset,
29005 bufferLength: endOffset - currOffset,
29006 bufferContent: o.slice(currOffset, endOffset)
29008 currOffset = endOffset;
29012 while (hunks.length) {
29013 var hunk = hunks.shift();
29014 var regionStart = hunk.oStart;
29015 var regionEnd = hunk.oStart + hunk.oLength;
29016 var regionHunks = [hunk];
29017 advanceTo(regionStart); // Try to pull next overlapping hunk into this region
29019 while (hunks.length) {
29020 var nextHunk = hunks[0];
29021 var nextHunkStart = nextHunk.oStart;
29022 if (nextHunkStart > regionEnd) break; // no overlap
29024 regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
29025 regionHunks.push(hunks.shift());
29028 if (regionHunks.length === 1) {
29029 // Only one hunk touches this region, meaning that there is no conflict here.
29030 // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
29031 if (hunk.abLength > 0) {
29032 var buffer = hunk.ab === 'a' ? a : b;
29036 bufferStart: hunk.abStart,
29037 bufferLength: hunk.abLength,
29038 bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
29042 // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
29043 // Effectively merge all the `a` hunks into one giant hunk, then do the
29044 // same for the `b` hunks; then, correct for skew in the regions of `o`
29045 // that each side changed, and report appropriate spans for the three sides.
29047 a: [a.length, -1, o.length, -1],
29048 b: [b.length, -1, o.length, -1]
29051 while (regionHunks.length) {
29052 hunk = regionHunks.shift();
29053 var oStart = hunk.oStart;
29054 var oEnd = oStart + hunk.oLength;
29055 var abStart = hunk.abStart;
29056 var abEnd = abStart + hunk.abLength;
29057 var _b = bounds[hunk.ab];
29058 _b[0] = Math.min(abStart, _b[0]);
29059 _b[1] = Math.max(abEnd, _b[1]);
29060 _b[2] = Math.min(oStart, _b[2]);
29061 _b[3] = Math.max(oEnd, _b[3]);
29064 var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
29065 var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
29066 var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
29067 var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
29071 aLength: aEnd - aStart,
29072 aContent: a.slice(aStart, aEnd),
29073 oStart: regionStart,
29074 oLength: regionEnd - regionStart,
29075 oContent: o.slice(regionStart, regionEnd),
29077 bLength: bEnd - bStart,
29078 bContent: b.slice(bStart, bEnd)
29080 results.push(result);
29083 currOffset = regionEnd;
29086 advanceTo(o.length);
29088 } // Applies the output of diff3MergeRegions to actually
29089 // construct the merged buffer; the returned result alternates
29090 // between 'ok' and 'conflict' blocks.
29091 // A "false conflict" is where `a` and `b` both change the same from `o`
29094 function diff3Merge(a, o, b, options) {
29096 excludeFalseConflicts: true,
29097 stringSeparator: /\s+/
29099 options = Object.assign(defaults, options);
29100 var aString = typeof a === 'string';
29101 var oString = typeof o === 'string';
29102 var bString = typeof b === 'string';
29103 if (aString) a = a.split(options.stringSeparator);
29104 if (oString) o = o.split(options.stringSeparator);
29105 if (bString) b = b.split(options.stringSeparator);
29107 var regions = diff3MergeRegions(a, o, b);
29110 function flushOk() {
29111 if (okBuffer.length) {
29120 function isFalseConflict(a, b) {
29121 if (a.length !== b.length) return false;
29123 for (var i = 0; i < a.length; i++) {
29124 if (a[i] !== b[i]) return false;
29130 regions.forEach(function (region) {
29131 if (region.stable) {
29134 (_okBuffer = okBuffer).push.apply(_okBuffer, _toConsumableArray(region.bufferContent));
29136 if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
29139 (_okBuffer2 = okBuffer).push.apply(_okBuffer2, _toConsumableArray(region.aContent));
29144 a: region.aContent,
29145 aIndex: region.aStart,
29146 o: region.oContent,
29147 oIndex: region.oStart,
29148 b: region.bContent,
29149 bIndex: region.bStart
29159 function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
29160 discardTags = discardTags || {};
29161 var _option = 'safe'; // 'safe', 'force_local', 'force_remote'
29163 var _conflicts = [];
29166 return typeof formatUser === 'function' ? formatUser(d) : d;
29169 function mergeLocation(remote, target) {
29170 function pointEqual(a, b) {
29171 var epsilon = 1e-6;
29172 return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon;
29175 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
29179 if (_option === 'force_remote') {
29180 return target.update({
29185 _conflicts.push(_t('merge_remote_changes.conflict.location', {
29186 user: user(remote.user)
29192 function mergeNodes(base, remote, target) {
29193 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
29197 if (_option === 'force_remote') {
29198 return target.update({
29199 nodes: remote.nodes
29203 var ccount = _conflicts.length;
29204 var o = base.nodes || [];
29205 var a = target.nodes || [];
29206 var b = remote.nodes || [];
29208 var hunks = diff3Merge(a, o, b, {
29209 excludeFalseConflicts: true
29212 for (var i = 0; i < hunks.length; i++) {
29213 var hunk = hunks[i];
29216 nodes.push.apply(nodes, hunk.ok);
29218 // for all conflicts, we can assume c.a !== c.b
29219 // because `diff3Merge` called with `true` option to exclude false conflicts..
29220 var c = hunk.conflict;
29222 if (fastDeepEqual(c.o, c.a)) {
29223 // only changed remotely
29224 nodes.push.apply(nodes, c.b);
29225 } else if (fastDeepEqual(c.o, c.b)) {
29226 // only changed locally
29227 nodes.push.apply(nodes, c.a);
29229 // changed both locally and remotely
29230 _conflicts.push(_t('merge_remote_changes.conflict.nodelist', {
29231 user: user(remote.user)
29239 return _conflicts.length === ccount ? target.update({
29244 function mergeChildren(targetWay, children, updates, graph) {
29245 function isUsed(node, targetWay) {
29246 var hasInterestingParent = graph.parentWays(node).some(function (way) {
29247 return way.id !== targetWay.id;
29249 return node.hasInterestingTags() || hasInterestingParent || graph.parentRelations(node).length > 0;
29252 var ccount = _conflicts.length;
29254 for (var i = 0; i < children.length; i++) {
29255 var id = children[i];
29256 var node = graph.hasEntity(id); // remove unused childNodes..
29258 if (targetWay.nodes.indexOf(id) === -1) {
29259 if (node && !isUsed(node, targetWay)) {
29260 updates.removeIds.push(id);
29264 } // restore used childNodes..
29267 var local = localGraph.hasEntity(id);
29268 var remote = remoteGraph.hasEntity(id);
29271 if (_option === 'force_remote' && remote && remote.visible) {
29272 updates.replacements.push(remote);
29273 } else if (_option === 'force_local' && local) {
29274 target = osmEntity(local);
29277 target = target.update({
29278 version: remote.version
29282 updates.replacements.push(target);
29283 } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
29284 target = osmEntity(local, {
29285 version: remote.version
29288 if (remote.visible) {
29289 target = mergeLocation(remote, target);
29291 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
29292 user: user(remote.user)
29296 if (_conflicts.length !== ccount) break;
29297 updates.replacements.push(target);
29304 function updateChildren(updates, graph) {
29305 for (var i = 0; i < updates.replacements.length; i++) {
29306 graph = graph.replace(updates.replacements[i]);
29309 if (updates.removeIds.length) {
29310 graph = actionDeleteMultiple(updates.removeIds)(graph);
29316 function mergeMembers(remote, target) {
29317 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
29321 if (_option === 'force_remote') {
29322 return target.update({
29323 members: remote.members
29327 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', {
29328 user: user(remote.user)
29334 function mergeTags(base, remote, target) {
29335 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
29339 if (_option === 'force_remote') {
29340 return target.update({
29345 var ccount = _conflicts.length;
29346 var o = base.tags || {};
29347 var a = target.tags || {};
29348 var b = remote.tags || {};
29349 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)).filter(function (k) {
29350 return !discardTags[k];
29352 var tags = Object.assign({}, a); // shallow copy
29354 var changed = false;
29356 for (var i = 0; i < keys.length; i++) {
29359 if (o[k] !== b[k] && a[k] !== b[k]) {
29360 // changed remotely..
29361 if (o[k] !== a[k]) {
29362 // changed locally..
29363 _conflicts.push(_t('merge_remote_changes.conflict.tags', {
29367 user: user(remote.user)
29370 // unchanged locally, accept remote change..
29371 if (b.hasOwnProperty(k)) {
29382 return changed && _conflicts.length === ccount ? target.update({
29385 } // `graph.base()` is the common ancestor of the two graphs.
29386 // `localGraph` contains user's edits up to saving
29387 // `remoteGraph` contains remote edits to modified nodes
29388 // `graph` must be a descendent of `localGraph` and may include
29389 // some conflict resolution actions performed on it.
29391 // --- ... --- `localGraph` -- ... -- `graph`
29393 // `graph.base()` --- ... --- `remoteGraph`
29397 var action = function action(graph) {
29402 var base = graph.base().entities[id];
29403 var local = localGraph.entity(id);
29404 var remote = remoteGraph.entity(id);
29405 var target = osmEntity(local, {
29406 version: remote.version
29407 }); // delete/undelete
29409 if (!remote.visible) {
29410 if (_option === 'force_remote') {
29411 return actionDeleteMultiple([id])(graph);
29412 } else if (_option === 'force_local') {
29413 if (target.type === 'way') {
29414 target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
29415 graph = updateChildren(updates, graph);
29418 return graph.replace(target);
29420 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
29421 user: user(remote.user)
29424 return graph; // do nothing
29429 if (target.type === 'node') {
29430 target = mergeLocation(remote, target);
29431 } else if (target.type === 'way') {
29432 // pull in any child nodes that may not be present locally..
29433 graph.rebase(remoteGraph.childNodes(remote), [graph], false);
29434 target = mergeNodes(base, remote, target);
29435 target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
29436 } else if (target.type === 'relation') {
29437 target = mergeMembers(remote, target);
29440 target = mergeTags(base, remote, target);
29442 if (!_conflicts.length) {
29443 graph = updateChildren(updates, graph).replace(target);
29449 action.withOption = function (opt) {
29454 action.conflicts = function () {
29461 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
29463 function actionMove(moveIDs, tryDelta, projection, cache) {
29464 var _delta = tryDelta;
29466 function setupCache(graph) {
29467 function canMove(nodeID) {
29468 // Allow movement of any node that is in the selectedIDs list..
29469 if (moveIDs.indexOf(nodeID) !== -1) return true; // Allow movement of a vertex where 2 ways meet..
29471 var parents = graph.parentWays(graph.entity(nodeID));
29472 if (parents.length < 3) return true; // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
29474 var parentsMoving = parents.every(function (way) {
29475 return cache.moving[way.id];
29477 if (!parentsMoving) delete cache.moving[nodeID];
29478 return parentsMoving;
29481 function cacheEntities(ids) {
29482 for (var i = 0; i < ids.length; i++) {
29484 if (cache.moving[id]) continue;
29485 cache.moving[id] = true;
29486 var entity = graph.hasEntity(id);
29487 if (!entity) continue;
29489 if (entity.type === 'node') {
29490 cache.nodes.push(id);
29491 cache.startLoc[id] = entity.loc;
29492 } else if (entity.type === 'way') {
29493 cache.ways.push(id);
29494 cacheEntities(entity.nodes);
29496 cacheEntities(entity.members.map(function (member) {
29503 function cacheIntersections(ids) {
29504 function isEndpoint(way, id) {
29505 return !way.isClosed() && !!way.affix(id);
29508 for (var i = 0; i < ids.length; i++) {
29509 var id = ids[i]; // consider only intersections with 1 moved and 1 unmoved way.
29511 var childNodes = graph.childNodes(graph.entity(id));
29513 for (var j = 0; j < childNodes.length; j++) {
29514 var node = childNodes[j];
29515 var parents = graph.parentWays(node);
29516 if (parents.length !== 2) continue;
29517 var moved = graph.entity(id);
29518 var unmoved = null;
29520 for (var k = 0; k < parents.length; k++) {
29521 var way = parents[k];
29523 if (!cache.moving[way.id]) {
29529 if (!unmoved) continue; // exclude ways that are overly connected..
29531 if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
29532 if (moved.isArea() || unmoved.isArea()) continue;
29533 cache.intersections.push({
29536 unmovedId: unmoved.id,
29537 movedIsEP: isEndpoint(moved, node.id),
29538 unmovedIsEP: isEndpoint(unmoved, node.id)
29550 cache.intersections = [];
29551 cache.replacedVertex = {};
29552 cache.startLoc = {};
29555 cacheEntities(moveIDs);
29556 cacheIntersections(cache.ways);
29557 cache.nodes = cache.nodes.filter(canMove);
29560 } // Place a vertex where the moved vertex used to be, to preserve way shape..
29569 // * node '*' added to preserve shape
29571 // / b ---- e way `b,e` moved here:
29578 function replaceMovedVertex(nodeId, wayId, graph, delta) {
29579 var way = graph.entity(wayId);
29580 var moved = graph.entity(nodeId);
29581 var movedIndex = way.nodes.indexOf(nodeId);
29582 var len, prevIndex, nextIndex;
29584 if (way.isClosed()) {
29585 len = way.nodes.length - 1;
29586 prevIndex = (movedIndex + len - 1) % len;
29587 nextIndex = (movedIndex + len + 1) % len;
29589 len = way.nodes.length;
29590 prevIndex = movedIndex - 1;
29591 nextIndex = movedIndex + 1;
29594 var prev = graph.hasEntity(way.nodes[prevIndex]);
29595 var next = graph.hasEntity(way.nodes[nextIndex]); // Don't add orig vertex at endpoint..
29597 if (!prev || !next) return graph;
29598 var key = wayId + '_' + nodeId;
29599 var orig = cache.replacedVertex[key];
29603 cache.replacedVertex[key] = orig;
29604 cache.startLoc[orig.id] = cache.startLoc[nodeId];
29610 start = projection(cache.startLoc[nodeId]);
29611 end = projection.invert(geoVecAdd(start, delta));
29613 end = cache.startLoc[nodeId];
29616 orig = orig.move(end);
29617 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..
29619 if (angle > 175 && angle < 185) return graph; // moving forward or backward along way?
29621 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
29622 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
29623 var d1 = geoPathLength(p1);
29624 var d2 = geoPathLength(p2);
29625 var insertAt = d1 <= d2 ? movedIndex : nextIndex; // moving around closed loop?
29627 if (way.isClosed() && insertAt === 0) insertAt = len;
29628 way = way.addNode(orig.id, insertAt);
29629 return graph.replace(orig).replace(way);
29630 } // Remove duplicate vertex that might have been added by
29631 // replaceMovedVertex. This is done after the unzorro checks.
29634 function removeDuplicateVertices(wayId, graph) {
29635 var way = graph.entity(wayId);
29636 var epsilon = 1e-6;
29639 function isInteresting(node, graph) {
29640 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
29643 for (var i = 0; i < way.nodes.length; i++) {
29644 curr = graph.entity(way.nodes[i]);
29646 if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
29647 if (!isInteresting(prev, graph)) {
29648 way = way.removeNode(prev.id);
29649 graph = graph.replace(way).remove(prev);
29650 } else if (!isInteresting(curr, graph)) {
29651 way = way.removeNode(curr.id);
29652 graph = graph.replace(way).remove(curr);
29660 } // Reorder nodes around intersections that have moved..
29662 // Start: way1.nodes: b,e (moving)
29663 // a - b - c ----- d way2.nodes: a,b,c,d (static)
29665 // e isEP1: true, isEP2, false
29667 // way1 `b,e` moved here:
29668 // a ----- c = b - d
29672 // reorder nodes way1.nodes: b,e
29673 // a ----- c - b - d way2.nodes: a,c,b,d
29679 function unZorroIntersection(intersection, graph) {
29680 var vertex = graph.entity(intersection.nodeId);
29681 var way1 = graph.entity(intersection.movedId);
29682 var way2 = graph.entity(intersection.unmovedId);
29683 var isEP1 = intersection.movedIsEP;
29684 var isEP2 = intersection.unmovedIsEP; // don't move the vertex if it is the endpoint of both ways.
29686 if (isEP1 && isEP2) return graph;
29687 var nodes1 = graph.childNodes(way1).filter(function (n) {
29688 return n !== vertex;
29690 var nodes2 = graph.childNodes(way2).filter(function (n) {
29691 return n !== vertex;
29693 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
29694 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
29695 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
29696 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
29697 var loc; // snap vertex to nearest edge (or some point between them)..
29699 if (!isEP1 && !isEP2) {
29700 var epsilon = 1e-6,
29703 for (var i = 0; i < maxIter; i++) {
29704 loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
29705 edge1 = geoChooseEdge(nodes1, projection(loc), projection);
29706 edge2 = geoChooseEdge(nodes2, projection(loc), projection);
29707 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
29709 } else if (!isEP1) {
29715 graph = graph.replace(vertex.move(loc)); // if zorro happened, reorder nodes..
29717 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
29718 way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
29719 graph = graph.replace(way1);
29722 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
29723 way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
29724 graph = graph.replace(way2);
29730 function cleanupIntersections(graph) {
29731 for (var i = 0; i < cache.intersections.length; i++) {
29732 var obj = cache.intersections[i];
29733 graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
29734 graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
29735 graph = unZorroIntersection(obj, graph);
29736 graph = removeDuplicateVertices(obj.movedId, graph);
29737 graph = removeDuplicateVertices(obj.unmovedId, graph);
29741 } // check if moving way endpoint can cross an unmoved way, if so limit delta..
29744 function limitDelta(graph) {
29745 function moveNode(loc) {
29746 return geoVecAdd(projection(loc), _delta);
29749 for (var i = 0; i < cache.intersections.length; i++) {
29750 var obj = cache.intersections[i]; // Don't limit movement if this is vertex joins 2 endpoints..
29752 if (obj.movedIsEP && obj.unmovedIsEP) continue; // Don't limit movement if this vertex is not an endpoint anyway..
29754 if (!obj.movedIsEP) continue;
29755 var node = graph.entity(obj.nodeId);
29756 var start = projection(node.loc);
29757 var end = geoVecAdd(start, _delta);
29758 var movedNodes = graph.childNodes(graph.entity(obj.movedId));
29759 var movedPath = movedNodes.map(function (n) {
29760 return moveNode(n.loc);
29762 var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
29763 var unmovedPath = unmovedNodes.map(function (n) {
29764 return projection(n.loc);
29766 var hits = geoPathIntersections(movedPath, unmovedPath);
29768 for (var j = 0; i < hits.length; i++) {
29769 if (geoVecEqual(hits[j], end)) continue;
29770 var edge = geoChooseEdge(unmovedNodes, end, projection);
29771 _delta = geoVecSubtract(projection(edge.loc), start);
29776 var action = function action(graph) {
29777 if (_delta[0] === 0 && _delta[1] === 0) return graph;
29780 if (cache.intersections.length) {
29784 for (var i = 0; i < cache.nodes.length; i++) {
29785 var node = graph.entity(cache.nodes[i]);
29786 var start = projection(node.loc);
29787 var end = geoVecAdd(start, _delta);
29788 graph = graph.replace(node.move(projection.invert(end)));
29791 if (cache.intersections.length) {
29792 graph = cleanupIntersections(graph);
29798 action.delta = function () {
29805 function actionMoveMember(relationId, fromIndex, toIndex) {
29806 return function (graph) {
29807 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
29811 function actionMoveNode(nodeID, toLoc) {
29812 var action = function action(graph, t) {
29813 if (t === null || !isFinite(t)) t = 1;
29814 t = Math.min(Math.max(+t, 0), 1);
29815 var node = graph.entity(nodeID);
29816 return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t)));
29819 action.transitionable = true;
29823 function actionNoop() {
29824 return function (graph) {
29829 function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
29830 var epsilon = ep || 1e-4;
29831 var threshold = degThresh || 13; // degrees within right or straight to alter
29832 // We test normalized dot products so we can compare as cos(angle)
29834 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
29835 var upperThreshold = Math.cos(threshold * Math.PI / 180);
29837 var action = function action(graph, t) {
29838 if (t === null || !isFinite(t)) t = 1;
29839 t = Math.min(Math.max(+t, 0), 1);
29840 var way = graph.entity(wayID);
29841 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
29843 if (way.tags.nonsquare) {
29844 var tags = Object.assign({}, way.tags); // since we're squaring, remove indication that this is physically unsquare
29846 delete tags.nonsquare;
29852 graph = graph.replace(way);
29853 var isClosed = way.isClosed();
29854 var nodes = graph.childNodes(way).slice(); // shallow copy
29856 if (isClosed) nodes.pop();
29858 if (vertexID !== undefined) {
29859 nodes = nodeSubset(nodes, vertexID, isClosed);
29860 if (nodes.length !== 3) return graph;
29861 } // note: all geometry functions here use the unclosed node/point/coord list
29864 var nodeCount = {};
29870 var node, point, loc, score, motions, i, j;
29872 for (i = 0; i < nodes.length; i++) {
29874 nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
29877 coord: projection(node.loc)
29881 if (points.length === 3) {
29882 // move only one vertex for right triangle
29883 for (i = 0; i < 1000; i++) {
29884 motions = points.map(calcMotion);
29885 points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
29886 score = corner.dotp;
29888 if (score < epsilon) {
29893 node = graph.entity(nodes[corner.i].id);
29894 loc = projection.invert(points[corner.i].coord);
29895 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
29897 var straights = [];
29898 var simplified = []; // Remove points from nearly straight sections..
29899 // This produces a simplified shape to orthogonalize
29901 for (i = 0; i < points.length; i++) {
29905 if (isClosed || i > 0 && i < points.length - 1) {
29906 var a = points[(i - 1 + points.length) % points.length];
29907 var b = points[(i + 1) % points.length];
29908 dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
29911 if (dotp > upperThreshold) {
29912 straights.push(point);
29914 simplified.push(point);
29916 } // Orthogonalize the simplified shape
29919 var bestPoints = clonePoints(simplified);
29920 var originalPoints = clonePoints(simplified);
29923 for (i = 0; i < 1000; i++) {
29924 motions = simplified.map(calcMotion);
29926 for (j = 0; j < motions.length; j++) {
29927 simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
29930 var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
29932 if (newScore < score) {
29933 bestPoints = clonePoints(simplified);
29937 if (score < epsilon) {
29942 var bestCoords = bestPoints.map(function (p) {
29945 if (isClosed) bestCoords.push(bestCoords[0]); // move the nodes that should move
29947 for (i = 0; i < bestPoints.length; i++) {
29948 point = bestPoints[i];
29950 if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
29951 node = graph.entity(point.id);
29952 loc = projection.invert(point.coord);
29953 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
29955 } // move the nodes along straight segments
29958 for (i = 0; i < straights.length; i++) {
29959 point = straights[i];
29960 if (nodeCount[point.id] > 1) continue; // skip self-intersections
29962 node = graph.entity(point.id);
29964 if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) {
29965 // remove uninteresting points..
29966 graph = actionDeleteNode(node.id)(graph);
29968 // move interesting points to the nearest edge..
29969 var choice = geoVecProject(point.coord, bestCoords);
29972 loc = projection.invert(choice.target);
29973 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
29981 function clonePoints(array) {
29982 return array.map(function (p) {
29985 coord: [p.coord[0], p.coord[1]]
29990 function calcMotion(point, i, array) {
29991 // don't try to move the endpoints of a non-closed way.
29992 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)
29994 if (nodeCount[array[i].id] > 1) return [0, 0];
29995 var a = array[(i - 1 + array.length) % array.length].coord;
29996 var origin = point.coord;
29997 var b = array[(i + 1) % array.length].coord;
29998 var p = geoVecSubtract(a, origin);
29999 var q = geoVecSubtract(b, origin);
30000 var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
30001 p = geoVecNormalize(p);
30002 q = geoVecNormalize(q);
30003 var dotp = p[0] * q[0] + p[1] * q[1];
30004 var val = Math.abs(dotp);
30006 if (val < lowerThreshold) {
30007 // nearly orthogonal
30010 var vec = geoVecNormalize(geoVecAdd(p, q));
30011 return geoVecScale(vec, 0.1 * dotp * scale);
30014 return [0, 0]; // do nothing
30016 }; // if we are only orthogonalizing one vertex,
30017 // get that vertex and the previous and next
30020 function nodeSubset(nodes, vertexID, isClosed) {
30021 var first = isClosed ? 0 : 1;
30022 var last = isClosed ? nodes.length : nodes.length - 1;
30024 for (var i = first; i < last; i++) {
30025 if (nodes[i].id === vertexID) {
30026 return [nodes[(i - 1 + nodes.length) % nodes.length], nodes[i], nodes[(i + 1) % nodes.length]];
30033 action.disabled = function (graph) {
30034 var way = graph.entity(wayID);
30035 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
30037 graph = graph.replace(way);
30038 var isClosed = way.isClosed();
30039 var nodes = graph.childNodes(way).slice(); // shallow copy
30041 if (isClosed) nodes.pop();
30042 var allowStraightAngles = false;
30044 if (vertexID !== undefined) {
30045 allowStraightAngles = true;
30046 nodes = nodeSubset(nodes, vertexID, isClosed);
30047 if (nodes.length !== 3) return 'end_vertex';
30050 var coords = nodes.map(function (n) {
30051 return projection(n.loc);
30053 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
30055 if (score === null) {
30056 return 'not_squarish';
30057 } else if (score === 0) {
30058 return 'square_enough';
30064 action.transitionable = true;
30069 // `turn` must be an `osmTurn` object
30070 // see osm/intersection.js, pathToTurn()
30072 // This specifies a restriction of type `restriction` when traveling from
30073 // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
30074 // (The action does not check that these entities form a valid intersection.)
30076 // From, to, and via ways should be split before calling this action.
30077 // (old versions of the code would split the ways here, but we no longer do it)
30079 // For testing convenience, accepts a restrictionID to assign to the new
30080 // relation. Normally, this will be undefined and the relation will
30081 // automatically be assigned a new ID.
30084 function actionRestrictTurn(turn, restrictionType, restrictionID) {
30085 return function (graph) {
30086 var fromWay = graph.entity(turn.from.way);
30087 var toWay = graph.entity(turn.to.way);
30088 var viaNode = turn.via.node && graph.entity(turn.via.node);
30089 var viaWays = turn.via.ways && turn.via.ways.map(function (id) {
30090 return graph.entity(id);
30105 } else if (viaWays) {
30106 viaWays.forEach(function (viaWay) {
30120 return graph.replace(osmRelation({
30123 type: 'restriction',
30124 restriction: restrictionType
30131 function actionRevert(id) {
30132 var action = function action(graph) {
30133 var entity = graph.hasEntity(id),
30134 base = graph.base().entities[id];
30136 if (entity && !base) {
30137 // entity will be removed..
30138 if (entity.type === 'node') {
30139 graph.parentWays(entity).forEach(function (parent) {
30140 parent = parent.removeNode(id);
30141 graph = graph.replace(parent);
30143 if (parent.isDegenerate()) {
30144 graph = actionDeleteWay(parent.id)(graph);
30149 graph.parentRelations(entity).forEach(function (parent) {
30150 parent = parent.removeMembersWithID(id);
30151 graph = graph.replace(parent);
30153 if (parent.isDegenerate()) {
30154 graph = actionDeleteRelation(parent.id)(graph);
30159 return graph.revert(id);
30165 function actionRotate(rotateIds, pivot, angle, projection) {
30166 var action = function action(graph) {
30167 return graph.update(function (graph) {
30168 utilGetAllNodes(rotateIds, graph).forEach(function (node) {
30169 var point = geoRotate([projection(node.loc)], angle, pivot)[0];
30170 graph = graph.replace(node.move(projection.invert(point)));
30178 function actionScale(ids, pivotLoc, scaleFactor, projection) {
30179 return function (graph) {
30180 return graph.update(function (graph) {
30182 utilGetAllNodes(ids, graph).forEach(function (node) {
30183 point = projection(node.loc);
30184 radial = [point[0] - pivotLoc[0], point[1] - pivotLoc[1]];
30185 point = [pivotLoc[0] + scaleFactor * radial[0], pivotLoc[1] + scaleFactor * radial[1]];
30186 graph = graph.replace(node.move(projection.invert(point)));
30192 /* Align nodes along their common axis */
30194 function actionStraightenNodes(nodeIDs, projection) {
30195 function positionAlongWay(a, o, b) {
30196 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
30197 } // returns the endpoints of the long axis of symmetry of the `points` bounding rect
30200 function getEndpoints(points) {
30201 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
30202 // The shape's surrounding rectangle has 2 axes of symmetry.
30203 // Snap points to the long axis
30205 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
30206 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
30207 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
30208 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
30209 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
30218 var action = function action(graph, t) {
30219 if (t === null || !isFinite(t)) t = 1;
30220 t = Math.min(Math.max(+t, 0), 1);
30221 var nodes = nodeIDs.map(function (id) {
30222 return graph.entity(id);
30224 var points = nodes.map(function (n) {
30225 return projection(n.loc);
30227 var endpoints = getEndpoints(points);
30228 var startPoint = endpoints[0];
30229 var endPoint = endpoints[1]; // Move points onto the line connecting the endpoints
30231 for (var i = 0; i < points.length; i++) {
30232 var node = nodes[i];
30233 var point = points[i];
30234 var u = positionAlongWay(point, startPoint, endPoint);
30235 var point2 = geoVecInterp(startPoint, endPoint, u);
30236 var loc2 = projection.invert(point2);
30237 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
30243 action.disabled = function (graph) {
30244 var nodes = nodeIDs.map(function (id) {
30245 return graph.entity(id);
30247 var points = nodes.map(function (n) {
30248 return projection(n.loc);
30250 var endpoints = getEndpoints(points);
30251 var startPoint = endpoints[0];
30252 var endPoint = endpoints[1];
30253 var maxDistance = 0;
30255 for (var i = 0; i < points.length; i++) {
30256 var point = points[i];
30257 var u = positionAlongWay(point, startPoint, endPoint);
30258 var p = geoVecInterp(startPoint, endPoint, u);
30259 var dist = geoVecLength(p, point);
30261 if (!isNaN(dist) && dist > maxDistance) {
30262 maxDistance = dist;
30266 if (maxDistance < 0.0001) {
30267 return 'straight_enough';
30271 action.transitionable = true;
30276 * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
30279 function actionStraightenWay(selectedIDs, projection) {
30280 function positionAlongWay(a, o, b) {
30281 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
30282 } // Return all selected ways as a continuous, ordered array of nodes
30285 function allNodes(graph) {
30287 var startNodes = [];
30289 var remainingWays = [];
30290 var selectedWays = selectedIDs.filter(function (w) {
30291 return graph.entity(w).type === 'way';
30293 var selectedNodes = selectedIDs.filter(function (n) {
30294 return graph.entity(n).type === 'node';
30297 for (var i = 0; i < selectedWays.length; i++) {
30298 var way = graph.entity(selectedWays[i]);
30299 nodes = way.nodes.slice(0);
30300 remainingWays.push(nodes);
30301 startNodes.push(nodes[0]);
30302 endNodes.push(nodes[nodes.length - 1]);
30303 } // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
30304 // and need to be removed so currNode difference calculation below works)
30305 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
30308 startNodes = startNodes.filter(function (n) {
30309 return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
30311 endNodes = endNodes.filter(function (n) {
30312 return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
30313 }); // Choose the initial endpoint to start from
30315 var currNode = utilArrayDifference(startNodes, endNodes).concat(utilArrayDifference(endNodes, startNodes))[0];
30317 nodes = []; // Create nested function outside of loop to avoid "function in loop" lint error
30319 var getNextWay = function getNextWay(currNode, remainingWays) {
30320 return remainingWays.filter(function (way) {
30321 return way[0] === currNode || way[way.length - 1] === currNode;
30323 }; // Add nodes to end of nodes array, until all ways are added
30326 while (remainingWays.length) {
30327 nextWay = getNextWay(currNode, remainingWays);
30328 remainingWays = utilArrayDifference(remainingWays, [nextWay]);
30330 if (nextWay[0] !== currNode) {
30334 nodes = nodes.concat(nextWay);
30335 currNode = nodes[nodes.length - 1];
30336 } // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
30339 if (selectedNodes.length === 2) {
30340 var startNodeIdx = nodes.indexOf(selectedNodes[0]);
30341 var endNodeIdx = nodes.indexOf(selectedNodes[1]);
30342 var sortedStartEnd = [startNodeIdx, endNodeIdx];
30343 sortedStartEnd.sort(function (a, b) {
30346 nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1] + 1);
30349 return nodes.map(function (n) {
30350 return graph.entity(n);
30354 function shouldKeepNode(node, graph) {
30355 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
30358 var action = function action(graph, t) {
30359 if (t === null || !isFinite(t)) t = 1;
30360 t = Math.min(Math.max(+t, 0), 1);
30361 var nodes = allNodes(graph);
30362 var points = nodes.map(function (n) {
30363 return projection(n.loc);
30365 var startPoint = points[0];
30366 var endPoint = points[points.length - 1];
30370 for (i = 1; i < points.length - 1; i++) {
30371 var node = nodes[i];
30372 var point = points[i];
30374 if (t < 1 || shouldKeepNode(node, graph)) {
30375 var u = positionAlongWay(point, startPoint, endPoint);
30376 var p = geoVecInterp(startPoint, endPoint, u);
30377 var loc2 = projection.invert(p);
30378 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
30381 if (toDelete.indexOf(node) === -1) {
30382 toDelete.push(node);
30387 for (i = 0; i < toDelete.length; i++) {
30388 graph = actionDeleteNode(toDelete[i].id)(graph);
30394 action.disabled = function (graph) {
30395 // check way isn't too bendy
30396 var nodes = allNodes(graph);
30397 var points = nodes.map(function (n) {
30398 return projection(n.loc);
30400 var startPoint = points[0];
30401 var endPoint = points[points.length - 1];
30402 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
30405 if (threshold === 0) {
30406 return 'too_bendy';
30409 var maxDistance = 0;
30411 for (i = 1; i < points.length - 1; i++) {
30412 var point = points[i];
30413 var u = positionAlongWay(point, startPoint, endPoint);
30414 var p = geoVecInterp(startPoint, endPoint, u);
30415 var dist = geoVecLength(p, point); // to bendy if point is off by 20% of total start/end distance in projected space
30417 if (isNaN(dist) || dist > threshold) {
30418 return 'too_bendy';
30419 } else if (dist > maxDistance) {
30420 maxDistance = dist;
30424 var keepingAllNodes = nodes.every(function (node, i) {
30425 return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
30428 if (maxDistance < 0.0001 && // Allow straightening even if already straight in order to remove extraneous nodes
30430 return 'straight_enough';
30434 action.transitionable = true;
30439 // `turn` must be an `osmTurn` object with a `restrictionID` property.
30440 // see osm/intersection.js, pathToTurn()
30443 function actionUnrestrictTurn(turn) {
30444 return function (graph) {
30445 return actionDeleteRelation(turn.restrictionID)(graph);
30449 /* Reflect the given area around its axis of symmetry */
30451 function actionReflect(reflectIds, projection) {
30452 var _useLongAxis = true;
30454 var action = function action(graph, t) {
30455 if (t === null || !isFinite(t)) t = 1;
30456 t = Math.min(Math.max(+t, 0), 1);
30457 var nodes = utilGetAllNodes(reflectIds, graph);
30458 var points = nodes.map(function (n) {
30459 return projection(n.loc);
30461 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
30462 // The shape's surrounding rectangle has 2 axes of symmetry.
30463 // Reflect across the longer axis by default.
30465 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
30466 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
30467 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
30468 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
30470 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
30472 if (_useLongAxis && isLong || !_useLongAxis && !isLong) {
30478 } // reflect c across pq
30479 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
30482 var dx = q[0] - p[0];
30483 var dy = q[1] - p[1];
30484 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
30485 var b = 2 * dx * dy / (dx * dx + dy * dy);
30487 for (var i = 0; i < nodes.length; i++) {
30488 var node = nodes[i];
30489 var c = projection(node.loc);
30490 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]];
30491 var loc2 = projection.invert(c2);
30492 node = node.move(geoVecInterp(node.loc, loc2, t));
30493 graph = graph.replace(node);
30499 action.useLongAxis = function (val) {
30500 if (!arguments.length) return _useLongAxis;
30501 _useLongAxis = val;
30505 action.transitionable = true;
30509 function actionUpgradeTags(entityId, oldTags, replaceTags) {
30510 return function (graph) {
30511 var entity = graph.entity(entityId);
30512 var tags = Object.assign({}, entity.tags); // shallow copy
30517 for (var oldTagKey in oldTags) {
30518 if (!(oldTagKey in tags)) continue; // wildcard match
30520 if (oldTags[oldTagKey] === '*') {
30521 // note the value since we might need to transfer it
30522 transferValue = tags[oldTagKey];
30523 delete tags[oldTagKey]; // exact match
30524 } else if (oldTags[oldTagKey] === tags[oldTagKey]) {
30525 delete tags[oldTagKey]; // match is within semicolon-delimited values
30527 var vals = tags[oldTagKey].split(';').filter(Boolean);
30528 var oldIndex = vals.indexOf(oldTags[oldTagKey]);
30530 if (vals.length === 1 || oldIndex === -1) {
30531 delete tags[oldTagKey];
30533 if (replaceTags && replaceTags[oldTagKey]) {
30534 // replacing a value within a semicolon-delimited value, note the index
30535 semiIndex = oldIndex;
30538 vals.splice(oldIndex, 1);
30539 tags[oldTagKey] = vals.join(';');
30545 for (var replaceKey in replaceTags) {
30546 var replaceValue = replaceTags[replaceKey];
30548 if (replaceValue === '*') {
30549 if (tags[replaceKey] && tags[replaceKey] !== 'no') {
30550 // allow any pre-existing value except `no` (troll tag)
30553 // otherwise assume `yes` is okay
30554 tags[replaceKey] = 'yes';
30556 } else if (replaceValue === '$1') {
30557 tags[replaceKey] = transferValue;
30559 if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
30560 // don't override preexisting values
30561 var existingVals = tags[replaceKey].split(';').filter(Boolean);
30563 if (existingVals.indexOf(replaceValue) === -1) {
30564 existingVals.splice(semiIndex, 0, replaceValue);
30565 tags[replaceKey] = existingVals.join(';');
30568 tags[replaceKey] = replaceValue;
30574 return graph.replace(entity.update({
30580 function behaviorEdit(context) {
30581 function behavior() {
30582 context.map().minzoom(context.minEditableZoom());
30585 behavior.off = function () {
30586 context.map().minzoom(0);
30593 The hover behavior adds the `.hover` class on pointerover to all elements to which
30594 the identical datum is bound, and removes it on pointerout.
30596 The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
30597 representation may consist of several elements scattered throughout the DOM hierarchy.
30598 Only one of these elements can have the :hover pseudo-class, but all of them will
30599 have the .hover class.
30602 function behaviorHover(context) {
30603 var dispatch$1 = dispatch('hover');
30605 var _selection = select(null);
30607 var _newNodeId = null;
30608 var _initialNodeID = null;
30614 var _targets = []; // use pointer events on supported platforms; fallback to mouse events
30616 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
30618 function keydown(d3_event) {
30619 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
30620 _selection.selectAll('.hover').classed('hover-suppressed', true).classed('hover', false);
30622 _selection.classed('hover-disabled', true);
30624 dispatch$1.call('hover', this, null);
30628 function keyup(d3_event) {
30629 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
30630 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false).classed('hover', true);
30632 _selection.classed('hover-disabled', false);
30634 dispatch$1.call('hover', this, _targets);
30638 function behavior(selection) {
30639 _selection = selection;
30642 if (_initialNodeID) {
30643 _newNodeId = _initialNodeID;
30644 _initialNodeID = null;
30649 _selection.on(_pointerPrefix + 'over.hover', pointerover).on(_pointerPrefix + 'out.hover', pointerout) // treat pointerdown as pointerover for touch devices
30650 .on(_pointerPrefix + 'down.hover', pointerover);
30652 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true).on('keydown.hover', keydown).on('keyup.hover', keyup);
30654 function eventTarget(d3_event) {
30655 var datum = d3_event.target && d3_event.target.__data__;
30656 if (_typeof(datum) !== 'object') return null;
30658 if (!(datum instanceof osmEntity) && datum.properties && datum.properties.entity instanceof osmEntity) {
30659 return datum.properties.entity;
30665 function pointerover(d3_event) {
30666 // ignore mouse hovers with buttons pressed unless dragging
30667 if (context.mode().id.indexOf('drag') === -1 && (!d3_event.pointerType || d3_event.pointerType === 'mouse') && d3_event.buttons) return;
30668 var target = eventTarget(d3_event);
30670 if (target && _targets.indexOf(target) === -1) {
30671 _targets.push(target);
30673 updateHover(d3_event, _targets);
30677 function pointerout(d3_event) {
30678 var target = eventTarget(d3_event);
30680 var index = _targets.indexOf(target);
30682 if (index !== -1) {
30683 _targets.splice(index);
30685 updateHover(d3_event, _targets);
30689 function allowsVertex(d) {
30690 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
30693 function modeAllowsHover(target) {
30694 var mode = context.mode();
30696 if (mode.id === 'add-point') {
30697 return mode.preset.matchGeometry('vertex') || target.type !== 'way' && target.geometry(context.graph()) !== 'vertex';
30703 function updateHover(d3_event, targets) {
30704 _selection.selectAll('.hover').classed('hover', false);
30706 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
30708 var mode = context.mode();
30710 if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
30711 var node = targets.find(function (target) {
30712 return target instanceof osmEntity && target.type === 'node';
30714 _newNodeId = node && node.id;
30717 targets = targets.filter(function (datum) {
30718 if (datum instanceof osmEntity) {
30719 // If drawing a way, don't hover on a node that was just placed. #3974
30720 return datum.id !== _newNodeId && (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && modeAllowsHover(datum);
30727 for (var i in targets) {
30728 var datum = targets[i]; // What are we hovering over?
30730 if (datum.__featurehash__) {
30731 // hovering custom data
30732 selector += ', .data' + datum.__featurehash__;
30733 } else if (datum instanceof QAItem) {
30734 selector += ', .' + datum.service + '.itemId-' + datum.id;
30735 } else if (datum instanceof osmNote) {
30736 selector += ', .note-' + datum.id;
30737 } else if (datum instanceof osmEntity) {
30738 selector += ', .' + datum.id;
30740 if (datum.type === 'relation') {
30741 for (var j in datum.members) {
30742 selector += ', .' + datum.members[j].id;
30748 var suppressed = _altDisables && d3_event && d3_event.altKey;
30750 if (selector.trim().length) {
30751 // remove the first comma
30752 selector = selector.slice(1);
30754 _selection.selectAll(selector).classed(suppressed ? 'hover-suppressed' : 'hover', true);
30757 dispatch$1.call('hover', this, !suppressed && targets);
30761 behavior.off = function (selection) {
30762 selection.selectAll('.hover').classed('hover', false);
30763 selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
30764 selection.classed('hover-disabled', false);
30765 selection.on(_pointerPrefix + 'over.hover', null).on(_pointerPrefix + 'out.hover', null).on(_pointerPrefix + 'down.hover', null);
30766 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', null, true).on('keydown.hover', null).on('keyup.hover', null);
30769 behavior.altDisables = function (val) {
30770 if (!arguments.length) return _altDisables;
30771 _altDisables = val;
30775 behavior.ignoreVertex = function (val) {
30776 if (!arguments.length) return _ignoreVertex;
30777 _ignoreVertex = val;
30781 behavior.initialNodeID = function (nodeId) {
30782 _initialNodeID = nodeId;
30786 return utilRebind(behavior, dispatch$1, 'on');
30789 var _disableSpace = false;
30790 var _lastSpace = null;
30791 function behaviorDraw(context) {
30792 var dispatch$1 = dispatch('move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish');
30793 var keybinding = utilKeybinding('draw');
30795 var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on('hover', context.ui().sidebar.hover);
30797 var _edit = behaviorEdit(context);
30799 var _closeTolerance = 4;
30800 var _tolerance = 12;
30801 var _mouseLeave = false;
30802 var _lastMouse = null;
30804 var _lastPointerUpEvent;
30806 var _downPointer; // use pointer events on supported platforms; fallback to mouse events
30809 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // related code
30810 // - `mode/drag_node.js` `datum()`
30813 function datum(d3_event) {
30814 var mode = context.mode();
30815 var isNote = mode && mode.id.indexOf('note') !== -1;
30816 if (d3_event.altKey || isNote) return {};
30819 if (d3_event.type === 'keydown') {
30820 element = _lastMouse && _lastMouse.target;
30822 element = d3_event.target;
30823 } // When drawing, snap only to touch targets..
30824 // (this excludes area fills and active drawing elements)
30827 var d = element.__data__;
30828 return d && d.properties && d.properties.target ? d : {};
30831 function pointerdown(d3_event) {
30832 if (_downPointer) return;
30833 var pointerLocGetter = utilFastMouse(this);
30835 id: d3_event.pointerId || 'mouse',
30836 pointerLocGetter: pointerLocGetter,
30837 downTime: +new Date(),
30838 downLoc: pointerLocGetter(d3_event)
30840 dispatch$1.call('down', this, d3_event, datum(d3_event));
30843 function pointerup(d3_event) {
30844 if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return;
30845 var downPointer = _downPointer;
30846 _downPointer = null;
30847 _lastPointerUpEvent = d3_event;
30848 if (downPointer.isCancelled) return;
30849 var t2 = +new Date();
30850 var p2 = downPointer.pointerLocGetter(d3_event);
30851 var dist = geoVecLength(downPointer.downLoc, p2);
30853 if (dist < _closeTolerance || dist < _tolerance && t2 - downPointer.downTime < 500) {
30854 // Prevent a quick second click
30855 select(window).on('click.draw-block', function () {
30856 d3_event.stopPropagation();
30858 context.map().dblclickZoomEnable(false);
30859 window.setTimeout(function () {
30860 context.map().dblclickZoomEnable(true);
30861 select(window).on('click.draw-block', null);
30863 click(d3_event, p2);
30867 function pointermove(d3_event) {
30868 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse') && !_downPointer.isCancelled) {
30869 var p2 = _downPointer.pointerLocGetter(d3_event);
30871 var dist = geoVecLength(_downPointer.downLoc, p2);
30873 if (dist >= _closeTolerance) {
30874 _downPointer.isCancelled = true;
30875 dispatch$1.call('downcancel', this);
30879 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
30880 // events immediately after non-mouse pointerup events; detect and ignore them.
30882 if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
30883 _lastMouse = d3_event;
30884 dispatch$1.call('move', this, d3_event, datum(d3_event));
30887 function pointercancel(d3_event) {
30888 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse')) {
30889 if (!_downPointer.isCancelled) {
30890 dispatch$1.call('downcancel', this);
30893 _downPointer = null;
30897 function mouseenter() {
30898 _mouseLeave = false;
30901 function mouseleave() {
30902 _mouseLeave = true;
30905 function allowsVertex(d) {
30906 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
30908 // - `mode/drag_node.js` `doMove()`
30909 // - `behavior/draw.js` `click()`
30910 // - `behavior/draw_way.js` `move()`
30913 function click(d3_event, loc) {
30914 var d = datum(d3_event);
30915 var target = d && d.properties && d.properties.entity;
30916 var mode = context.mode();
30918 if (target && target.type === 'node' && allowsVertex(target)) {
30920 dispatch$1.call('clickNode', this, target, d);
30922 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {
30924 var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID());
30927 var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
30928 dispatch$1.call('clickWay', this, choice.loc, edge, d);
30931 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
30932 var locLatLng = context.projection.invert(loc);
30933 dispatch$1.call('click', this, locLatLng, d);
30935 } // treat a spacebar press like a click
30938 function space(d3_event) {
30939 d3_event.preventDefault();
30940 d3_event.stopPropagation();
30941 var currSpace = context.map().mouse();
30943 if (_disableSpace && _lastSpace) {
30944 var dist = geoVecLength(_lastSpace, currSpace);
30946 if (dist > _tolerance) {
30947 _disableSpace = false;
30951 if (_disableSpace || _mouseLeave || !_lastMouse) return; // user must move mouse or release space bar to allow another click
30953 _lastSpace = currSpace;
30954 _disableSpace = true;
30955 select(window).on('keyup.space-block', function () {
30956 d3_event.preventDefault();
30957 d3_event.stopPropagation();
30958 _disableSpace = false;
30959 select(window).on('keyup.space-block', null);
30960 }); // get the current mouse position
30962 var loc = context.map().mouse() || // or the map center if the mouse has never entered the map
30963 context.projection(context.map().center());
30964 click(d3_event, loc);
30967 function backspace(d3_event) {
30968 d3_event.preventDefault();
30969 dispatch$1.call('undo');
30972 function del(d3_event) {
30973 d3_event.preventDefault();
30974 dispatch$1.call('cancel');
30977 function ret(d3_event) {
30978 d3_event.preventDefault();
30979 dispatch$1.call('finish');
30982 function behavior(selection) {
30983 context.install(_hover);
30984 context.install(_edit);
30985 _downPointer = null;
30986 keybinding.on('⌫', backspace).on('⌦', del).on('⎋', ret).on('↩', ret).on('space', space).on('⌥space', space);
30987 selection.on('mouseenter.draw', mouseenter).on('mouseleave.draw', mouseleave).on(_pointerPrefix + 'down.draw', pointerdown).on(_pointerPrefix + 'move.draw', pointermove);
30988 select(window).on(_pointerPrefix + 'up.draw', pointerup, true).on('pointercancel.draw', pointercancel, true);
30989 select(document).call(keybinding);
30993 behavior.off = function (selection) {
30994 context.ui().sidebar.hover.cancel();
30995 context.uninstall(_hover);
30996 context.uninstall(_edit);
30997 selection.on('mouseenter.draw', null).on('mouseleave.draw', null).on(_pointerPrefix + 'down.draw', null).on(_pointerPrefix + 'move.draw', null);
30998 select(window).on(_pointerPrefix + 'up.draw', null).on('pointercancel.draw', null); // note: keyup.space-block, click.draw-block should remain
31000 select(document).call(keybinding.unbind);
31003 behavior.hover = function () {
31007 return utilRebind(behavior, dispatch$1, 'on');
31010 function initRange(domain, range) {
31011 switch (arguments.length) {
31016 this.range(domain);
31020 this.range(range).domain(domain);
31027 function constants(x) {
31028 return function () {
31033 function number$1(x) {
31038 function identity$3(x) {
31042 function normalize$1(a, b) {
31043 return (b -= a = +a) ? function (x) {
31044 return (x - a) / b;
31045 } : constants(isNaN(b) ? NaN : 0.5);
31048 function clamper(a, b) {
31050 if (a > b) t = a, a = b, b = t;
31051 return function (x) {
31052 return Math.max(a, Math.min(b, x));
31054 } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
31055 // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
31058 function bimap(domain, range, interpolate) {
31059 var d0 = domain[0],
31063 if (d1 < d0) d0 = normalize$1(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize$1(d0, d1), r0 = interpolate(r0, r1);
31064 return function (x) {
31069 function polymap(domain, range, interpolate) {
31070 var j = Math.min(domain.length, range.length) - 1,
31073 i = -1; // Reverse descending domains.
31075 if (domain[j] < domain[0]) {
31076 domain = domain.slice().reverse();
31077 range = range.slice().reverse();
31081 d[i] = normalize$1(domain[i], domain[i + 1]);
31082 r[i] = interpolate(range[i], range[i + 1]);
31085 return function (x) {
31086 var i = bisectRight(domain, x, 1, j) - 1;
31087 return r[i](d[i](x));
31091 function copy(source, target) {
31092 return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown());
31094 function transformer$1() {
31097 interpolate$1 = interpolate,
31101 clamp = identity$3,
31106 function rescale() {
31107 var n = Math.min(domain.length, range.length);
31108 if (clamp !== identity$3) clamp = clamper(domain[0], domain[n - 1]);
31109 piecewise = n > 2 ? polymap : bimap;
31110 output = input = null;
31114 function scale(x) {
31115 return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x)));
31118 scale.invert = function (y) {
31119 return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
31122 scale.domain = function (_) {
31123 return arguments.length ? (domain = Array.from(_, number$1), rescale()) : domain.slice();
31126 scale.range = function (_) {
31127 return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
31130 scale.rangeRound = function (_) {
31131 return range = Array.from(_), interpolate$1 = interpolateRound, rescale();
31134 scale.clamp = function (_) {
31135 return arguments.length ? (clamp = _ ? true : identity$3, rescale()) : clamp !== identity$3;
31138 scale.interpolate = function (_) {
31139 return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1;
31142 scale.unknown = function (_) {
31143 return arguments.length ? (unknown = _, scale) : unknown;
31146 return function (t, u) {
31147 transform = t, untransform = u;
31151 function continuous() {
31152 return transformer$1()(identity$3, identity$3);
31155 function formatDecimal (x) {
31156 return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10);
31157 } // Computes the decimal coefficient and exponent of the specified number x with
31158 // significant digits p, where x is positive and p is in [1, 21] or undefined.
31159 // For example, formatDecimalParts(1.23) returns ["123", 0].
31161 function formatDecimalParts(x, p) {
31162 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
31165 coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
31166 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
31168 return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)];
31171 function exponent (x) {
31172 return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
31175 function formatGroup (grouping, thousands) {
31176 return function (value, width) {
31177 var i = value.length,
31183 while (i > 0 && g > 0) {
31184 if (length + g + 1 > width) g = Math.max(1, width - length);
31185 t.push(value.substring(i -= g, i + g));
31186 if ((length += g + 1) > width) break;
31187 g = grouping[j = (j + 1) % grouping.length];
31190 return t.reverse().join(thousands);
31194 function formatNumerals (numerals) {
31195 return function (value) {
31196 return value.replace(/[0-9]/g, function (i) {
31197 return numerals[+i];
31202 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
31203 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
31204 function formatSpecifier(specifier) {
31205 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
31207 return new FormatSpecifier({
31215 precision: match[8] && match[8].slice(1),
31220 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
31222 function FormatSpecifier(specifier) {
31223 this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
31224 this.align = specifier.align === undefined ? ">" : specifier.align + "";
31225 this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
31226 this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
31227 this.zero = !!specifier.zero;
31228 this.width = specifier.width === undefined ? undefined : +specifier.width;
31229 this.comma = !!specifier.comma;
31230 this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
31231 this.trim = !!specifier.trim;
31232 this.type = specifier.type === undefined ? "" : specifier.type + "";
31235 FormatSpecifier.prototype.toString = function () {
31236 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;
31239 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
31240 function formatTrim (s) {
31241 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
31248 if (i0 === 0) i0 = i;
31253 if (!+s[i]) break out;
31254 if (i0 > 0) i0 = 0;
31259 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
31262 // `thisNumberValue` abstract operation
31263 // https://tc39.github.io/ecma262/#sec-thisnumbervalue
31264 var thisNumberValue = function (value) {
31265 if (typeof value != 'number' && classofRaw(value) != 'Number') {
31266 throw TypeError('Incorrect invocation');
31271 // `String.prototype.repeat` method implementation
31272 // https://tc39.github.io/ecma262/#sec-string.prototype.repeat
31273 var stringRepeat = ''.repeat || function repeat(count) {
31274 var str = String(requireObjectCoercible(this));
31276 var n = toInteger(count);
31277 if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions');
31278 for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str;
31282 var nativeToFixed = 1.0.toFixed;
31283 var floor$6 = Math.floor;
31285 var pow$2 = function (x, n, acc) {
31286 return n === 0 ? acc : n % 2 === 1 ? pow$2(x, n - 1, acc * x) : pow$2(x * x, n / 2, acc);
31289 var log$2 = function (x) {
31292 while (x2 >= 4096) {
31302 var FORCED$c = nativeToFixed && (
31303 0.00008.toFixed(3) !== '0.000' ||
31304 0.9.toFixed(0) !== '1' ||
31305 1.255.toFixed(2) !== '1.25' ||
31306 1000000000000000128.0.toFixed(0) !== '1000000000000000128'
31307 ) || !fails(function () {
31308 // V8 ~ Android 4.3-
31309 nativeToFixed.call({});
31312 // `Number.prototype.toFixed` method
31313 // https://tc39.github.io/ecma262/#sec-number.prototype.tofixed
31314 _export({ target: 'Number', proto: true, forced: FORCED$c }, {
31315 // eslint-disable-next-line max-statements
31316 toFixed: function toFixed(fractionDigits) {
31317 var number = thisNumberValue(this);
31318 var fractDigits = toInteger(fractionDigits);
31319 var data = [0, 0, 0, 0, 0, 0];
31324 var multiply = function (n, c) {
31327 while (++index < 6) {
31328 c2 += n * data[index];
31329 data[index] = c2 % 1e7;
31330 c2 = floor$6(c2 / 1e7);
31334 var divide = function (n) {
31337 while (--index >= 0) {
31339 data[index] = floor$6(c / n);
31344 var dataToString = function () {
31347 while (--index >= 0) {
31348 if (s !== '' || index === 0 || data[index] !== 0) {
31349 var t = String(data[index]);
31350 s = s === '' ? t : s + stringRepeat.call('0', 7 - t.length) + t;
31355 if (fractDigits < 0 || fractDigits > 20) throw RangeError('Incorrect fraction digits');
31356 // eslint-disable-next-line no-self-compare
31357 if (number != number) return 'NaN';
31358 if (number <= -1e21 || number >= 1e21) return String(number);
31363 if (number > 1e-21) {
31364 e = log$2(number * pow$2(2, 69, 1)) - 69;
31365 z = e < 0 ? number * pow$2(2, -e, 1) : number / pow$2(2, e, 1);
31366 z *= 0x10000000000000;
31375 multiply(pow$2(10, j, 1), 0);
31384 result = dataToString();
31387 multiply(1 << -e, 0);
31388 result = dataToString() + stringRepeat.call('0', fractDigits);
31391 if (fractDigits > 0) {
31393 result = sign + (k <= fractDigits
31394 ? '0.' + stringRepeat.call('0', fractDigits - k) + result
31395 : result.slice(0, k - fractDigits) + '.' + result.slice(k - fractDigits));
31397 result = sign + result;
31402 var nativeToPrecision = 1.0.toPrecision;
31404 var FORCED$d = fails(function () {
31406 return nativeToPrecision.call(1, undefined) !== '1';
31407 }) || !fails(function () {
31408 // V8 ~ Android 4.3-
31409 nativeToPrecision.call({});
31412 // `Number.prototype.toPrecision` method
31413 // https://tc39.github.io/ecma262/#sec-number.prototype.toprecision
31414 _export({ target: 'Number', proto: true, forced: FORCED$d }, {
31415 toPrecision: function toPrecision(precision) {
31416 return precision === undefined
31417 ? nativeToPrecision.call(thisNumberValue(this))
31418 : nativeToPrecision.call(thisNumberValue(this), precision);
31422 var prefixExponent;
31423 function formatPrefixAuto (x, p) {
31424 var d = formatDecimalParts(x, p);
31425 if (!d) return x + "";
31426 var coefficient = d[0],
31428 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
31429 n = coefficient.length;
31430 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!
31433 function formatRounded (x, p) {
31434 var d = formatDecimalParts(x, p);
31435 if (!d) return x + "";
31436 var coefficient = d[0],
31438 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");
31441 var formatTypes = {
31442 "%": function _(x, p) {
31443 return (x * 100).toFixed(p);
31445 "b": function b(x) {
31446 return Math.round(x).toString(2);
31448 "c": function c(x) {
31451 "d": formatDecimal,
31452 "e": function e(x, p) {
31453 return x.toExponential(p);
31455 "f": function f(x, p) {
31456 return x.toFixed(p);
31458 "g": function g(x, p) {
31459 return x.toPrecision(p);
31461 "o": function o(x) {
31462 return Math.round(x).toString(8);
31464 "p": function p(x, _p) {
31465 return formatRounded(x * 100, _p);
31467 "r": formatRounded,
31468 "s": formatPrefixAuto,
31469 "X": function X(x) {
31470 return Math.round(x).toString(16).toUpperCase();
31472 "x": function x(_x) {
31473 return Math.round(_x).toString(16);
31477 function identity$4 (x) {
31481 var map = Array.prototype.map,
31482 prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
31483 function formatLocale (locale) {
31484 var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map.call(locale.grouping, Number), locale.thousands + ""),
31485 currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
31486 currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
31487 decimal = locale.decimal === undefined ? "." : locale.decimal + "",
31488 numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map.call(locale.numerals, String)),
31489 percent = locale.percent === undefined ? "%" : locale.percent + "",
31490 minus = locale.minus === undefined ? "−" : locale.minus + "",
31491 nan = locale.nan === undefined ? "NaN" : locale.nan + "";
31493 function newFormat(specifier) {
31494 specifier = formatSpecifier(specifier);
31495 var fill = specifier.fill,
31496 align = specifier.align,
31497 sign = specifier.sign,
31498 symbol = specifier.symbol,
31499 zero = specifier.zero,
31500 width = specifier.width,
31501 comma = specifier.comma,
31502 precision = specifier.precision,
31503 trim = specifier.trim,
31504 type = specifier.type; // The "n" type is an alias for ",g".
31506 if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g".
31507 else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits.
31509 if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix.
31510 // For SI-prefix, the suffix is lazily computed.
31512 var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
31513 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use?
31514 // Is this an integer type?
31515 // Can this type generate exponential notation?
31517 var formatType = formatTypes[type],
31518 maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified,
31519 // or clamp the specified precision to the supported range.
31520 // For significant precision, it must be in [1, 21].
31521 // For fixed precision, it must be in [0, 20].
31523 precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
31525 function format(value) {
31526 var valuePrefix = prefix,
31527 valueSuffix = suffix,
31532 if (type === "c") {
31533 valueSuffix = formatType(value) + valueSuffix;
31536 value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is!
31538 var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting.
31540 value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros.
31542 if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
31544 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix.
31546 valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
31547 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be
31548 // grouped, and fractional or exponential “suffix” part that is not.
31551 i = -1, n = value.length;
31554 if (c = value.charCodeAt(i), 48 > c || c > 57) {
31555 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
31556 value = value.slice(0, i);
31561 } // If the fill character is not "0", grouping is applied before padding.
31564 if (comma && !zero) value = group(value, Infinity); // Compute the padding.
31566 var length = valuePrefix.length + value.length + valueSuffix.length,
31567 padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding.
31569 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment.
31573 value = valuePrefix + value + valueSuffix + padding;
31577 value = valuePrefix + padding + value + valueSuffix;
31581 value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
31585 value = padding + valuePrefix + value + valueSuffix;
31589 return numerals(value);
31592 format.toString = function () {
31593 return specifier + "";
31599 function formatPrefix(specifier, value) {
31600 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
31601 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
31602 k = Math.pow(10, -e),
31603 prefix = prefixes[8 + e / 3];
31604 return function (value) {
31605 return f(k * value) + prefix;
31611 formatPrefix: formatPrefix
31621 currency: ["$", ""]
31623 function defaultLocale(definition) {
31624 locale = formatLocale(definition);
31625 format = locale.format;
31626 formatPrefix = locale.formatPrefix;
31630 function precisionFixed (step) {
31631 return Math.max(0, -exponent(Math.abs(step)));
31634 function precisionPrefix (step, value) {
31635 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
31638 function precisionRound (step, max) {
31639 step = Math.abs(step), max = Math.abs(max) - step;
31640 return Math.max(0, exponent(max) - exponent(step)) + 1;
31643 function tickFormat(start, stop, count, specifier) {
31644 var step = tickStep(start, stop, count),
31646 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
31648 switch (specifier.type) {
31651 var value = Math.max(Math.abs(start), Math.abs(stop));
31652 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
31653 return formatPrefix(specifier, value);
31662 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
31669 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
31674 return format(specifier);
31677 function linearish(scale) {
31678 var domain = scale.domain;
31680 scale.ticks = function (count) {
31682 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
31685 scale.tickFormat = function (count, specifier) {
31687 return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
31690 scale.nice = function (count) {
31691 if (count == null) count = 10;
31694 var i1 = d.length - 1;
31701 if (stop < start) {
31702 step = start, start = stop, stop = step;
31703 step = i0, i0 = i1, i1 = step;
31706 while (maxIter-- > 0) {
31707 step = tickIncrement(start, stop, count);
31709 if (step === prestep) {
31713 } else if (step > 0) {
31714 start = Math.floor(start / step) * step;
31715 stop = Math.ceil(stop / step) * step;
31716 } else if (step < 0) {
31717 start = Math.ceil(start * step) / step;
31718 stop = Math.floor(stop * step) / step;
31731 function linear$2() {
31732 var scale = continuous();
31734 scale.copy = function () {
31735 return copy(scale, linear$2());
31738 initRange.apply(scale, arguments);
31739 return linearish(scale);
31742 var nativeExpm1 = Math.expm1;
31743 var exp$1 = Math.exp;
31745 // `Math.expm1` method implementation
31746 // https://tc39.github.io/ecma262/#sec-math.expm1
31747 var mathExpm1 = (!nativeExpm1
31749 || nativeExpm1(10) > 22025.465794806719 || nativeExpm1(10) < 22025.4657948067165168
31751 || nativeExpm1(-2e-17) != -2e-17
31752 ) ? function expm1(x) {
31753 return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp$1(x) - 1;
31756 function quantize() {
31764 function scale(x) {
31765 return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
31768 function rescale() {
31770 domain = new Array(n);
31773 domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
31779 scale.domain = function (_) {
31782 return arguments.length ? ((_ref = _, _ref2 = _slicedToArray(_ref, 2), x0 = _ref2[0], x1 = _ref2[1], _ref), x0 = +x0, x1 = +x1, rescale()) : [x0, x1];
31785 scale.range = function (_) {
31786 return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();
31789 scale.invertExtent = function (y) {
31790 var i = range.indexOf(y);
31791 return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]];
31794 scale.unknown = function (_) {
31795 return arguments.length ? (unknown = _, scale) : scale;
31798 scale.thresholds = function () {
31799 return domain.slice();
31802 scale.copy = function () {
31803 return quantize().domain([x0, x1]).range(range).unknown(unknown);
31806 return initRange.apply(linearish(scale), arguments);
31809 // https://github.com/tc39/proposal-string-pad-start-end
31814 var ceil$1 = Math.ceil;
31816 // `String.prototype.{ padStart, padEnd }` methods implementation
31817 var createMethod$6 = function (IS_END) {
31818 return function ($this, maxLength, fillString) {
31819 var S = String(requireObjectCoercible($this));
31820 var stringLength = S.length;
31821 var fillStr = fillString === undefined ? ' ' : String(fillString);
31822 var intMaxLength = toLength(maxLength);
31823 var fillLen, stringFiller;
31824 if (intMaxLength <= stringLength || fillStr == '') return S;
31825 fillLen = intMaxLength - stringLength;
31826 stringFiller = stringRepeat.call(fillStr, ceil$1(fillLen / fillStr.length));
31827 if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen);
31828 return IS_END ? S + stringFiller : stringFiller + S;
31833 // `String.prototype.padStart` method
31834 // https://tc39.github.io/ecma262/#sec-string.prototype.padstart
31835 start: createMethod$6(false),
31836 // `String.prototype.padEnd` method
31837 // https://tc39.github.io/ecma262/#sec-string.prototype.padend
31838 end: createMethod$6(true)
31841 var padStart = stringPad.start;
31843 var abs$3 = Math.abs;
31844 var DatePrototype$1 = Date.prototype;
31845 var getTime$1 = DatePrototype$1.getTime;
31846 var nativeDateToISOString = DatePrototype$1.toISOString;
31848 // `Date.prototype.toISOString` method implementation
31849 // https://tc39.github.io/ecma262/#sec-date.prototype.toisostring
31850 // PhantomJS / old WebKit fails here:
31851 var dateToIsoString = (fails(function () {
31852 return nativeDateToISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z';
31853 }) || !fails(function () {
31854 nativeDateToISOString.call(new Date(NaN));
31855 })) ? function toISOString() {
31856 if (!isFinite(getTime$1.call(this))) throw RangeError('Invalid time value');
31858 var year = date.getUTCFullYear();
31859 var milliseconds = date.getUTCMilliseconds();
31860 var sign = year < 0 ? '-' : year > 9999 ? '+' : '';
31861 return sign + padStart(abs$3(year), sign ? 6 : 4, 0) +
31862 '-' + padStart(date.getUTCMonth() + 1, 2, 0) +
31863 '-' + padStart(date.getUTCDate(), 2, 0) +
31864 'T' + padStart(date.getUTCHours(), 2, 0) +
31865 ':' + padStart(date.getUTCMinutes(), 2, 0) +
31866 ':' + padStart(date.getUTCSeconds(), 2, 0) +
31867 '.' + padStart(milliseconds, 3, 0) +
31869 } : nativeDateToISOString;
31871 // `Date.prototype.toISOString` method
31872 // https://tc39.github.io/ecma262/#sec-date.prototype.toisostring
31873 // PhantomJS / old WebKit has a broken implementations
31874 _export({ target: 'Date', proto: true, forced: Date.prototype.toISOString !== dateToIsoString }, {
31875 toISOString: dateToIsoString
31878 function behaviorBreathe() {
31879 var duration = 800;
31881 var selector = '.selected.shadow, .selected .shadow';
31883 var _selected = select(null);
31891 function ratchetyInterpolator(a, b, steps, units) {
31894 var sample = quantize().domain([0, 1]).range(d3_quantize(d3_interpolateNumber(a, b), steps));
31895 return function (t) {
31896 return String(sample(t)) + (units || '');
31900 function reset(selection) {
31901 selection.style('stroke-opacity', null).style('stroke-width', null).style('fill-opacity', null).style('r', null);
31904 function setAnimationParams(transition, fromTo) {
31905 var toFrom = fromTo === 'from' ? 'to' : 'from';
31906 transition.styleTween('stroke-opacity', function (d) {
31907 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
31908 }).styleTween('stroke-width', function (d) {
31909 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
31910 }).styleTween('fill-opacity', function (d) {
31911 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
31912 }).styleTween('r', function (d) {
31913 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
31917 function calcAnimationParams(selection) {
31918 selection.call(reset).each(function (d) {
31919 var s = select(this);
31920 var tag = s.node().tagName;
31926 var width; // determine base opacity and width
31928 if (tag === 'circle') {
31929 opacity = parseFloat(s.style('fill-opacity') || 0.5);
31930 width = parseFloat(s.style('r') || 15.5);
31932 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
31933 width = parseFloat(s.style('stroke-width') || 10);
31934 } // calculate from/to interpolation params..
31938 p.from.opacity = opacity * 0.6;
31939 p.to.opacity = opacity * 1.25;
31940 p.from.width = width * 0.7;
31941 p.to.width = width * (tag === 'circle' ? 1.5 : 1);
31946 function run(surface, fromTo) {
31947 var toFrom = fromTo === 'from' ? 'to' : 'from';
31948 var currSelected = surface.selectAll(selector);
31949 var currClassed = surface.attr('class');
31951 if (_done || currSelected.empty()) {
31952 _selected.call(reset);
31954 _selected = select(null);
31958 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
31959 _selected.call(reset);
31961 _classed = currClassed;
31962 _selected = currSelected.call(calcAnimationParams);
31965 var didCallNextRun = false;
31967 _selected.transition().duration(duration).call(setAnimationParams, fromTo).on('end', function () {
31968 // `end` event is called for each selected element, but we want
31969 // it to run only once
31970 if (!didCallNextRun) {
31971 surface.call(run, toFrom);
31972 didCallNextRun = true;
31973 } // if entity was deselected, remove breathe styling
31976 if (!select(this).classed('selected')) {
31977 reset(select(this));
31982 function behavior(surface) {
31984 _timer = timer(function () {
31985 // wait for elements to actually become selected
31986 if (surface.selectAll(selector).empty()) {
31990 surface.call(run, 'from');
31998 behavior.restartIfNeeded = function (surface) {
31999 if (_selected.empty()) {
32000 surface.call(run, 'from');
32008 behavior.off = function () {
32015 _selected.interrupt().call(reset);
32021 /* Creates a keybinding behavior for an operation */
32022 function behaviorOperation(context) {
32025 function keypress(d3_event) {
32026 // prevent operations during low zoom selection
32027 if (!context.map().withinEditableZoom()) return;
32028 if (_operation.availableForKeypress && !_operation.availableForKeypress()) return;
32029 d3_event.preventDefault();
32031 var disabled = _operation.disabled();
32034 context.ui().flash.duration(4000).iconName('#iD-operation-' + _operation.id).iconClass('operation disabled').label(_operation.tooltip)();
32036 context.ui().flash.duration(2000).iconName('#iD-operation-' + _operation.id).iconClass('operation').label(_operation.annotation() || _operation.title)();
32037 if (_operation.point) _operation.point(null);
32043 function behavior() {
32044 if (_operation && _operation.available()) {
32045 context.keybinding().on(_operation.keys, keypress);
32051 behavior.off = function () {
32052 context.keybinding().off(_operation.keys);
32055 behavior.which = function (_) {
32056 if (!arguments.length) return _operation;
32064 function operationCircularize(context, selectedIDs) {
32067 var _actions = selectedIDs.map(getAction).filter(Boolean);
32069 var _amount = _actions.length === 1 ? 'single' : 'multiple';
32071 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
32075 function getAction(entityID) {
32076 var entity = context.entity(entityID);
32077 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
32080 _extent = entity.extent(context.graph());
32082 _extent = _extent.extend(entity.extent(context.graph()));
32085 return actionCircularize(entityID, context.projection);
32088 var operation = function operation() {
32089 if (!_actions.length) return;
32091 var combinedAction = function combinedAction(graph, t) {
32092 _actions.forEach(function (action) {
32093 if (!action.disabled(graph)) {
32094 graph = action(graph, t);
32101 combinedAction.transitionable = true;
32102 context.perform(combinedAction, operation.annotation());
32103 window.setTimeout(function () {
32104 context.validator().validate();
32105 }, 300); // after any transition
32108 operation.available = function () {
32109 return _actions.length && selectedIDs.length === _actions.length;
32110 }; // don't cache this because the visible extent could change
32113 operation.disabled = function () {
32114 if (!_actions.length) return '';
32116 var actionDisableds = _actions.map(function (action) {
32117 return action.disabled(context.graph());
32118 }).filter(Boolean);
32120 if (actionDisableds.length === _actions.length) {
32121 // none of the features can be circularized
32122 if (new Set(actionDisableds).size > 1) {
32123 return 'multiple_blockers';
32126 return actionDisableds[0];
32127 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
32128 return 'too_large';
32129 } else if (someMissing()) {
32130 return 'not_downloaded';
32131 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32132 return 'connected_to_hidden';
32137 function someMissing() {
32138 if (context.inIntro()) return false;
32139 var osm = context.connection();
32142 var missing = _coords.filter(function (loc) {
32143 return !osm.isDataLoaded(loc);
32146 if (missing.length) {
32147 missing.forEach(function (loc) {
32148 context.loadTileAtLoc(loc);
32158 operation.tooltip = function () {
32159 var disable = operation.disabled();
32160 return disable ? _t('operations.circularize.' + disable + '.' + _amount) : _t('operations.circularize.description.' + _amount);
32163 operation.annotation = function () {
32164 return _t('operations.circularize.annotation.feature', {
32169 operation.id = 'circularize';
32170 operation.keys = [_t('operations.circularize.key')];
32171 operation.title = _t('operations.circularize.title');
32172 operation.behavior = behaviorOperation(context).which(operation);
32176 // For example, ⌘Z -> Ctrl+Z
32178 var uiCmd = function uiCmd(code) {
32179 var detected = utilDetect();
32181 if (detected.os === 'mac') {
32185 if (detected.os === 'win') {
32186 if (code === '⌘⇧Z') return 'Ctrl+Y';
32198 for (var i = 0; i < code.length; i++) {
32199 if (code[i] in replacements) {
32200 result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
32207 }; // return a display-focused string for a given keyboard code
32209 uiCmd.display = function (code) {
32210 if (code.length !== 1) return code;
32211 var detected = utilDetect();
32212 var mac = detected.os === 'mac';
32213 var replacements = {
32214 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'),
32215 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'),
32216 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
32217 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'),
32218 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
32219 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'),
32220 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'),
32221 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'),
32222 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'),
32223 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'),
32224 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
32225 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'),
32226 '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu')
32228 return replacements[code] || code;
32231 function operationDelete(context, selectedIDs) {
32232 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32233 var action = actionDeleteMultiple(selectedIDs);
32234 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32235 var coords = nodes.map(function (n) {
32238 var extent = utilTotalExtent(selectedIDs, context.graph());
32240 var operation = function operation() {
32241 var nextSelectedID;
32242 var nextSelectedLoc;
32244 if (selectedIDs.length === 1) {
32245 var id = selectedIDs[0];
32246 var entity = context.entity(id);
32247 var geometry = entity.geometry(context.graph());
32248 var parents = context.graph().parentWays(entity);
32249 var parent = parents[0]; // Select the next closest node in the way.
32251 if (geometry === 'vertex') {
32252 var nodes = parent.nodes;
32253 var i = nodes.indexOf(id);
32257 } else if (i === nodes.length - 1) {
32260 var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
32261 var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
32262 i = a < b ? i - 1 : i + 1;
32265 nextSelectedID = nodes[i];
32266 nextSelectedLoc = context.entity(nextSelectedID).loc;
32270 context.perform(action, operation.annotation());
32271 context.validator().validate();
32273 if (nextSelectedID && nextSelectedLoc) {
32274 if (context.hasEntity(nextSelectedID)) {
32275 context.enter(modeSelect(context, [nextSelectedID]).follow(true));
32277 context.map().centerEase(nextSelectedLoc);
32278 context.enter(modeBrowse(context));
32281 context.enter(modeBrowse(context));
32285 operation.available = function () {
32289 operation.disabled = function () {
32290 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32291 return 'too_large';
32292 } else if (someMissing()) {
32293 return 'not_downloaded';
32294 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32295 return 'connected_to_hidden';
32296 } else if (selectedIDs.some(protectedMember)) {
32297 return 'part_of_relation';
32298 } else if (selectedIDs.some(incompleteRelation)) {
32299 return 'incomplete_relation';
32300 } else if (selectedIDs.some(hasWikidataTag)) {
32301 return 'has_wikidata_tag';
32306 function someMissing() {
32307 if (context.inIntro()) return false;
32308 var osm = context.connection();
32311 var missing = coords.filter(function (loc) {
32312 return !osm.isDataLoaded(loc);
32315 if (missing.length) {
32316 missing.forEach(function (loc) {
32317 context.loadTileAtLoc(loc);
32326 function hasWikidataTag(id) {
32327 var entity = context.entity(id);
32328 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
32331 function incompleteRelation(id) {
32332 var entity = context.entity(id);
32333 return entity.type === 'relation' && !entity.isComplete(context.graph());
32336 function protectedMember(id) {
32337 var entity = context.entity(id);
32338 if (entity.type !== 'way') return false;
32339 var parents = context.graph().parentRelations(entity);
32341 for (var i = 0; i < parents.length; i++) {
32342 var parent = parents[i];
32343 var type = parent.tags.type;
32344 var role = parent.memberById(id).role || 'outer';
32346 if (type === 'route' || type === 'boundary' || type === 'multipolygon' && role === 'outer') {
32355 operation.tooltip = function () {
32356 var disable = operation.disabled();
32357 return disable ? _t('operations.delete.' + disable + '.' + multi) : _t('operations.delete.description.' + multi);
32360 operation.annotation = function () {
32361 return selectedIDs.length === 1 ? _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.delete.annotation.feature', {
32362 n: selectedIDs.length
32366 operation.id = 'delete';
32367 operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
32368 operation.title = _t('operations.delete.title');
32369 operation.behavior = behaviorOperation(context).which(operation);
32373 function operationOrthogonalize(context, selectedIDs) {
32378 var _actions = selectedIDs.map(chooseAction).filter(Boolean);
32380 var _amount = _actions.length === 1 ? 'single' : 'multiple';
32382 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
32386 function chooseAction(entityID) {
32387 var entity = context.entity(entityID);
32388 var geometry = entity.geometry(context.graph());
32391 _extent = entity.extent(context.graph());
32393 _extent = _extent.extend(entity.extent(context.graph()));
32394 } // square a line/area
32397 if (entity.type === 'way' && new Set(entity.nodes).size > 2) {
32398 if (_type && _type !== 'feature') return null;
32400 return actionOrthogonalize(entityID, context.projection); // square a single vertex
32401 } else if (geometry === 'vertex') {
32402 if (_type && _type !== 'corner') return null;
32404 var graph = context.graph();
32405 var parents = graph.parentWays(entity);
32407 if (parents.length === 1) {
32408 var way = parents[0];
32410 if (way.nodes.indexOf(entityID) !== -1) {
32411 return actionOrthogonalize(way.id, context.projection, entityID);
32419 var operation = function operation() {
32420 if (!_actions.length) return;
32422 var combinedAction = function combinedAction(graph, t) {
32423 _actions.forEach(function (action) {
32424 if (!action.disabled(graph)) {
32425 graph = action(graph, t);
32432 combinedAction.transitionable = true;
32433 context.perform(combinedAction, operation.annotation());
32434 window.setTimeout(function () {
32435 context.validator().validate();
32436 }, 300); // after any transition
32439 operation.available = function () {
32440 return _actions.length && selectedIDs.length === _actions.length;
32441 }; // don't cache this because the visible extent could change
32444 operation.disabled = function () {
32445 if (!_actions.length) return '';
32447 var actionDisableds = _actions.map(function (action) {
32448 return action.disabled(context.graph());
32449 }).filter(Boolean);
32451 if (actionDisableds.length === _actions.length) {
32452 // none of the features can be squared
32453 if (new Set(actionDisableds).size > 1) {
32454 return 'multiple_blockers';
32457 return actionDisableds[0];
32458 } else if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
32459 return 'too_large';
32460 } else if (someMissing()) {
32461 return 'not_downloaded';
32462 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32463 return 'connected_to_hidden';
32468 function someMissing() {
32469 if (context.inIntro()) return false;
32470 var osm = context.connection();
32473 var missing = _coords.filter(function (loc) {
32474 return !osm.isDataLoaded(loc);
32477 if (missing.length) {
32478 missing.forEach(function (loc) {
32479 context.loadTileAtLoc(loc);
32489 operation.tooltip = function () {
32490 var disable = operation.disabled();
32491 return disable ? _t('operations.orthogonalize.' + disable + '.' + _amount) : _t('operations.orthogonalize.description.' + _type + '.' + _amount);
32494 operation.annotation = function () {
32495 return _t('operations.orthogonalize.annotation.' + _type, {
32500 operation.id = 'orthogonalize';
32501 operation.keys = [_t('operations.orthogonalize.key')];
32502 operation.title = _t('operations.orthogonalize.title');
32503 operation.behavior = behaviorOperation(context).which(operation);
32507 function operationReflectShort(context, selectedIDs) {
32508 return operationReflect(context, selectedIDs, 'short');
32510 function operationReflectLong(context, selectedIDs) {
32511 return operationReflect(context, selectedIDs, 'long');
32513 function operationReflect(context, selectedIDs, axis) {
32514 axis = axis || 'long';
32515 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32516 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32517 var coords = nodes.map(function (n) {
32520 var extent = utilTotalExtent(selectedIDs, context.graph());
32522 var operation = function operation() {
32523 var action = actionReflect(selectedIDs, context.projection).useLongAxis(Boolean(axis === 'long'));
32524 context.perform(action, operation.annotation());
32525 window.setTimeout(function () {
32526 context.validator().validate();
32527 }, 300); // after any transition
32530 operation.available = function () {
32531 return nodes.length >= 3;
32532 }; // don't cache this because the visible extent could change
32535 operation.disabled = function () {
32536 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32537 return 'too_large';
32538 } else if (someMissing()) {
32539 return 'not_downloaded';
32540 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32541 return 'connected_to_hidden';
32542 } else if (selectedIDs.some(incompleteRelation)) {
32543 return 'incomplete_relation';
32548 function someMissing() {
32549 if (context.inIntro()) return false;
32550 var osm = context.connection();
32553 var missing = coords.filter(function (loc) {
32554 return !osm.isDataLoaded(loc);
32557 if (missing.length) {
32558 missing.forEach(function (loc) {
32559 context.loadTileAtLoc(loc);
32568 function incompleteRelation(id) {
32569 var entity = context.entity(id);
32570 return entity.type === 'relation' && !entity.isComplete(context.graph());
32574 operation.tooltip = function () {
32575 var disable = operation.disabled();
32576 return disable ? _t('operations.reflect.' + disable + '.' + multi) : _t('operations.reflect.description.' + axis + '.' + multi);
32579 operation.annotation = function () {
32580 return _t('operations.reflect.annotation.' + axis + '.feature', {
32581 n: selectedIDs.length
32585 operation.id = 'reflect-' + axis;
32586 operation.keys = [_t('operations.reflect.key.' + axis)];
32587 operation.title = _t('operations.reflect.title.' + axis);
32588 operation.behavior = behaviorOperation(context).which(operation);
32592 function operationMove(context, selectedIDs) {
32593 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32594 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32595 var coords = nodes.map(function (n) {
32598 var extent = utilTotalExtent(selectedIDs, context.graph());
32600 var operation = function operation() {
32601 context.enter(modeMove(context, selectedIDs));
32604 operation.available = function () {
32605 return selectedIDs.length > 1 || context.entity(selectedIDs[0]).type !== 'node';
32608 operation.disabled = function () {
32609 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32610 return 'too_large';
32611 } else if (someMissing()) {
32612 return 'not_downloaded';
32613 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32614 return 'connected_to_hidden';
32615 } else if (selectedIDs.some(incompleteRelation)) {
32616 return 'incomplete_relation';
32621 function someMissing() {
32622 if (context.inIntro()) return false;
32623 var osm = context.connection();
32626 var missing = coords.filter(function (loc) {
32627 return !osm.isDataLoaded(loc);
32630 if (missing.length) {
32631 missing.forEach(function (loc) {
32632 context.loadTileAtLoc(loc);
32641 function incompleteRelation(id) {
32642 var entity = context.entity(id);
32643 return entity.type === 'relation' && !entity.isComplete(context.graph());
32647 operation.tooltip = function () {
32648 var disable = operation.disabled();
32649 return disable ? _t('operations.move.' + disable + '.' + multi) : _t('operations.move.description.' + multi);
32652 operation.annotation = function () {
32653 return selectedIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.move.annotation.feature', {
32654 n: selectedIDs.length
32658 operation.id = 'move';
32659 operation.keys = [_t('operations.move.key')];
32660 operation.title = _t('operations.move.title');
32661 operation.behavior = behaviorOperation(context).which(operation);
32662 operation.mouseOnly = true;
32666 function modeRotate(context, entityIDs) {
32671 var keybinding = utilKeybinding('rotate');
32672 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];
32673 var annotation = entityIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.rotate.annotation.feature', {
32674 n: entityIDs.length
32681 var _prevTransform;
32685 function doRotate() {
32688 if (context.graph() !== _prevGraph) {
32689 fn = context.perform;
32691 fn = context.replace;
32692 } // projection changed, recalculate _pivot
32695 var projection = context.projection;
32696 var currTransform = projection.transform();
32698 if (!_prevTransform || currTransform.k !== _prevTransform.k || currTransform.x !== _prevTransform.x || currTransform.y !== _prevTransform.y) {
32699 var nodes = utilGetAllNodes(entityIDs, context.graph());
32700 var points = nodes.map(function (n) {
32701 return projection(n.loc);
32703 _pivot = getPivot(points);
32704 _prevAngle = undefined;
32707 var currMouse = context.map().mouse();
32708 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
32709 if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
32710 var delta = currAngle - _prevAngle;
32711 fn(actionRotate(entityIDs, _pivot, delta, projection));
32712 _prevTransform = currTransform;
32713 _prevAngle = currAngle;
32714 _prevGraph = context.graph();
32717 function getPivot(points) {
32720 if (points.length === 1) {
32721 _pivot = points[0];
32722 } else if (points.length === 2) {
32723 _pivot = geoVecInterp(points[0], points[1], 0.5);
32725 var polygonHull = d3_polygonHull(points);
32727 if (polygonHull.length === 2) {
32728 _pivot = geoVecInterp(points[0], points[1], 0.5);
32730 _pivot = d3_polygonCentroid(d3_polygonHull(points));
32737 function finish(d3_event) {
32738 d3_event.stopPropagation();
32739 context.replace(actionNoop(), annotation);
32740 context.enter(modeSelect(context, entityIDs));
32743 function cancel() {
32745 context.enter(modeSelect(context, entityIDs));
32748 function undone() {
32749 context.enter(modeBrowse(context));
32752 mode.enter = function () {
32753 context.features().forceVisible(entityIDs);
32754 behaviors.forEach(context.install);
32755 context.surface().on('mousemove.rotate', doRotate).on('click.rotate', finish);
32756 context.history().on('undone.rotate', undone);
32757 keybinding.on('⎋', cancel).on('↩', finish);
32758 select(document).call(keybinding);
32761 mode.exit = function () {
32762 behaviors.forEach(context.uninstall);
32763 context.surface().on('mousemove.rotate', null).on('click.rotate', null);
32764 context.history().on('undone.rotate', null);
32765 select(document).call(keybinding.unbind);
32766 context.features().forceVisible([]);
32769 mode.selectedIDs = function () {
32770 if (!arguments.length) return entityIDs; // no assign
32778 function operationRotate(context, selectedIDs) {
32779 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32780 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32781 var coords = nodes.map(function (n) {
32784 var extent = utilTotalExtent(selectedIDs, context.graph());
32786 var operation = function operation() {
32787 context.enter(modeRotate(context, selectedIDs));
32790 operation.available = function () {
32791 return nodes.length >= 2;
32794 operation.disabled = function () {
32795 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32796 return 'too_large';
32797 } else if (someMissing()) {
32798 return 'not_downloaded';
32799 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32800 return 'connected_to_hidden';
32801 } else if (selectedIDs.some(incompleteRelation)) {
32802 return 'incomplete_relation';
32807 function someMissing() {
32808 if (context.inIntro()) return false;
32809 var osm = context.connection();
32812 var missing = coords.filter(function (loc) {
32813 return !osm.isDataLoaded(loc);
32816 if (missing.length) {
32817 missing.forEach(function (loc) {
32818 context.loadTileAtLoc(loc);
32827 function incompleteRelation(id) {
32828 var entity = context.entity(id);
32829 return entity.type === 'relation' && !entity.isComplete(context.graph());
32833 operation.tooltip = function () {
32834 var disable = operation.disabled();
32835 return disable ? _t('operations.rotate.' + disable + '.' + multi) : _t('operations.rotate.description.' + multi);
32838 operation.annotation = function () {
32839 return selectedIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.rotate.annotation.feature', {
32840 n: selectedIDs.length
32844 operation.id = 'rotate';
32845 operation.keys = [_t('operations.rotate.key')];
32846 operation.title = _t('operations.rotate.title');
32847 operation.behavior = behaviorOperation(context).which(operation);
32848 operation.mouseOnly = true;
32852 function modeMove(context, entityIDs, baseGraph) {
32857 var keybinding = utilKeybinding('move');
32858 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];
32859 var annotation = entityIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.move.annotation.feature', {
32860 n: entityIDs.length
32869 var _nudgeInterval;
32871 function doMove(nudge) {
32872 nudge = nudge || [0, 0];
32875 if (_prevGraph !== context.graph()) {
32877 _origin = context.map().mouseCoordinates();
32878 fn = context.perform;
32880 fn = context.overwrite;
32883 var currMouse = context.map().mouse();
32884 var origMouse = context.projection(_origin);
32885 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
32886 fn(actionMove(entityIDs, delta, context.projection, _cache));
32887 _prevGraph = context.graph();
32890 function startNudge(nudge) {
32891 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
32892 _nudgeInterval = window.setInterval(function () {
32893 context.map().pan(nudge);
32898 function stopNudge() {
32899 if (_nudgeInterval) {
32900 window.clearInterval(_nudgeInterval);
32901 _nudgeInterval = null;
32907 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
32916 function finish(d3_event) {
32917 d3_event.stopPropagation();
32918 context.replace(actionNoop(), annotation);
32919 context.enter(modeSelect(context, entityIDs));
32923 function cancel() {
32925 while (context.graph() !== baseGraph) {
32929 context.enter(modeBrowse(context));
32932 context.enter(modeSelect(context, entityIDs));
32938 function undone() {
32939 context.enter(modeBrowse(context));
32942 mode.enter = function () {
32943 _origin = context.map().mouseCoordinates();
32946 context.features().forceVisible(entityIDs);
32947 behaviors.forEach(context.install);
32948 context.surface().on('mousemove.move', move).on('click.move', finish);
32949 context.history().on('undone.move', undone);
32950 keybinding.on('⎋', cancel).on('↩', finish);
32951 select(document).call(keybinding);
32954 mode.exit = function () {
32956 behaviors.forEach(function (behavior) {
32957 context.uninstall(behavior);
32959 context.surface().on('mousemove.move', null).on('click.move', null);
32960 context.history().on('undone.move', null);
32961 select(document).call(keybinding.unbind);
32962 context.features().forceVisible([]);
32965 mode.selectedIDs = function () {
32966 if (!arguments.length) return entityIDs; // no assign
32974 function behaviorPaste(context) {
32975 function doPaste(d3_event) {
32976 // prevent paste during low zoom selection
32977 if (!context.map().withinEditableZoom()) return;
32978 d3_event.preventDefault();
32979 var baseGraph = context.graph();
32980 var mouse = context.map().mouse();
32981 var projection = context.projection;
32982 var viewport = geoExtent(projection.clipExtent()).polygon();
32983 if (!geoPointInPolygon(mouse, viewport)) return;
32984 var oldIDs = context.copyIDs();
32985 if (!oldIDs.length) return;
32986 var extent = geoExtent();
32987 var oldGraph = context.copyGraph();
32989 var action = actionCopyEntities(oldIDs, oldGraph);
32990 context.perform(action);
32991 var copies = action.copies();
32992 var originals = new Set();
32993 Object.values(copies).forEach(function (entity) {
32994 originals.add(entity.id);
32997 for (var id in copies) {
32998 var oldEntity = oldGraph.entity(id);
32999 var newEntity = copies[id];
33001 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
33004 var parents = context.graph().parentWays(newEntity);
33005 var parentCopied = parents.some(function (parent) {
33006 return originals.has(parent.id);
33009 if (!parentCopied) {
33010 newIDs.push(newEntity.id);
33012 } // Put pasted objects where mouse pointer is..
33015 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
33016 var delta = geoVecSubtract(mouse, copyPoint);
33017 context.perform(actionMove(newIDs, delta, projection));
33018 context.enter(modeMove(context, newIDs, baseGraph));
33021 function behavior() {
33022 context.keybinding().on(uiCmd('⌘V'), doPaste);
33026 behavior.off = function () {
33027 context.keybinding().off(uiCmd('⌘V'));
33033 // `String.prototype.repeat` method
33034 // https://tc39.github.io/ecma262/#sec-string.prototype.repeat
33035 _export({ target: 'String', proto: true }, {
33036 repeat: stringRepeat
33040 `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
33042 * The `origin` function is expected to return an [x, y] tuple rather than an
33044 * The events are `start`, `move`, and `end`.
33045 (https://github.com/mbostock/d3/issues/563)
33046 * The `start` event is not dispatched until the first cursor movement occurs.
33047 (https://github.com/mbostock/d3/pull/368)
33048 * The `move` event has a `point` and `delta` [x, y] tuple properties rather
33049 than `x`, `y`, `dx`, and `dy` properties.
33050 * The `end` event is not dispatched if no movement occurs.
33051 * An `off` function is available that unbinds the drag's internal event handlers.
33054 function behaviorDrag() {
33055 var dispatch$1 = dispatch('start', 'move', 'end'); // see also behaviorSelect
33057 var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
33059 var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
33061 var _origin = null;
33062 var _selector = '';
33070 var _pointerId; // use pointer events on supported platforms; fallback to mouse events
33073 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
33075 var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
33077 var d3_event_userSelectSuppress = function d3_event_userSelectSuppress() {
33078 var selection$1 = selection();
33079 var select = selection$1.style(d3_event_userSelectProperty);
33080 selection$1.style(d3_event_userSelectProperty, 'none');
33081 return function () {
33082 selection$1.style(d3_event_userSelectProperty, select);
33086 function pointerdown(d3_event) {
33087 if (_pointerId) return;
33088 _pointerId = d3_event.pointerId || 'mouse';
33089 _targetNode = this; // only force reflow once per drag
33091 var pointerLocGetter = utilFastMouse(_surface || _targetNode.parentNode);
33093 var startOrigin = pointerLocGetter(d3_event);
33094 var started = false;
33095 var selectEnable = d3_event_userSelectSuppress();
33096 select(window).on(_pointerPrefix + 'move.drag', pointermove).on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
33099 offset = _origin.call(_targetNode, _targetEntity);
33100 offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
33105 d3_event.stopPropagation();
33107 function pointermove(d3_event) {
33108 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
33109 var p = pointerLocGetter(d3_event);
33112 var dist = geoVecLength(startOrigin, p);
33113 var tolerance = d3_event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; // don't start until the drag has actually moved somewhat
33115 if (dist < tolerance) return;
33117 dispatch$1.call('start', this, d3_event, _targetEntity); // Don't send a `move` event in the same cycle as `start` since dragging
33118 // a midpoint will convert the target to a node.
33121 d3_event.stopPropagation();
33122 d3_event.preventDefault();
33123 var dx = p[0] - startOrigin[0];
33124 var dy = p[1] - startOrigin[1];
33125 dispatch$1.call('move', this, d3_event, _targetEntity, [p[0] + offset[0], p[1] + offset[1]], [dx, dy]);
33129 function pointerup(d3_event) {
33130 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
33134 dispatch$1.call('end', this, d3_event, _targetEntity);
33135 d3_event.preventDefault();
33138 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
33143 function behavior(selection) {
33144 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
33145 var delegate = pointerdown;
33148 delegate = function delegate(d3_event) {
33150 var target = d3_event.target;
33152 for (; target && target !== root; target = target.parentNode) {
33153 var datum = target.__data__;
33154 _targetEntity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity;
33156 if (_targetEntity && target[matchesSelector](_selector)) {
33157 return pointerdown.call(target, d3_event);
33163 selection.on(_pointerPrefix + 'down.drag' + _selector, delegate);
33166 behavior.off = function (selection) {
33167 selection.on(_pointerPrefix + 'down.drag' + _selector, null);
33170 behavior.selector = function (_) {
33171 if (!arguments.length) return _selector;
33176 behavior.origin = function (_) {
33177 if (!arguments.length) return _origin;
33182 behavior.cancel = function () {
33183 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
33187 behavior.targetNode = function (_) {
33188 if (!arguments.length) return _targetNode;
33193 behavior.targetEntity = function (_) {
33194 if (!arguments.length) return _targetEntity;
33199 behavior.surface = function (_) {
33200 if (!arguments.length) return _surface;
33205 return utilRebind(behavior, dispatch$1, 'on');
33208 function modeDragNode(context) {
33213 var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover);
33214 var edit = behaviorEdit(context);
33216 var _nudgeInterval;
33218 var _restoreSelectedIDs = [];
33219 var _wasMidpoint = false;
33220 var _isCancelled = false;
33228 function startNudge(d3_event, entity, nudge) {
33229 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
33230 _nudgeInterval = window.setInterval(function () {
33231 context.map().pan(nudge);
33232 doMove(d3_event, entity, nudge);
33236 function stopNudge() {
33237 if (_nudgeInterval) {
33238 window.clearInterval(_nudgeInterval);
33239 _nudgeInterval = null;
33243 function moveAnnotation(entity) {
33244 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
33247 function connectAnnotation(nodeEntity, targetEntity) {
33248 var nodeGeometry = nodeEntity.geometry(context.graph());
33249 var targetGeometry = targetEntity.geometry(context.graph());
33251 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
33252 var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
33253 var targetParentWayIDs = context.graph().parentWays(targetEntity);
33254 var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); // if both vertices are part of the same way
33256 if (sharedParentWays.length !== 0) {
33257 // if the nodes are next to each other, they are merged
33258 if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
33259 return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
33262 return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
33266 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
33269 function shouldSnapToNode(target) {
33270 if (!_activeEntity) return false;
33271 return _activeEntity.geometry(context.graph()) !== 'vertex' || target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph());
33274 function origin(entity) {
33275 return context.projection(entity.loc);
33278 function keydown(d3_event) {
33279 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
33280 if (context.surface().classed('nope')) {
33281 context.surface().classed('nope-suppressed', true);
33284 context.surface().classed('nope', false).classed('nope-disabled', true);
33288 function keyup(d3_event) {
33289 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
33290 if (context.surface().classed('nope-suppressed')) {
33291 context.surface().classed('nope', true);
33294 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
33298 function start(d3_event, entity) {
33299 _wasMidpoint = entity.type === 'midpoint';
33300 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
33301 _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden;
33303 if (_isCancelled) {
33305 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('modes.drag_node.connected_to_hidden'))();
33308 return drag.cancel();
33311 if (_wasMidpoint) {
33312 var midpoint = entity;
33313 entity = osmNode();
33314 context.perform(actionAddMidpoint(midpoint, entity));
33315 entity = context.entity(entity.id); // get post-action entity
33317 var vertex = context.surface().selectAll('.' + entity.id);
33318 drag.targetNode(vertex.node()).targetEntity(entity);
33320 context.perform(actionNoop());
33323 _activeEntity = entity;
33324 _startLoc = entity.loc;
33325 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
33326 context.surface().selectAll('.' + _activeEntity.id).classed('active', true);
33327 context.enter(mode);
33329 // - `behavior/draw.js` `datum()`
33332 function datum(d3_event) {
33333 if (!d3_event || d3_event.altKey) {
33336 // When dragging, snap only to touch targets..
33337 // (this excludes area fills and active drawing elements)
33338 var d = d3_event.target.__data__;
33339 return d && d.properties && d.properties.target ? d : {};
33343 function doMove(d3_event, entity, nudge) {
33344 nudge = nudge || [0, 0];
33345 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
33346 var currMouse = geoVecSubtract(currPoint, nudge);
33347 var loc = context.projection.invert(currMouse);
33350 if (!_nudgeInterval) {
33351 // If not nudging at the edge of the viewport, try to snap..
33353 // - `mode/drag_node.js` `doMove()`
33354 // - `behavior/draw.js` `click()`
33355 // - `behavior/draw_way.js` `move()`
33356 var d = datum(d3_event);
33357 target = d && d.properties && d.properties.entity;
33358 var targetLoc = target && target.loc;
33359 var targetNodes = d && d.properties && d.properties.nodes;
33362 // snap to node/vertex - a point target with `.loc`
33363 if (shouldSnapToNode(target)) {
33366 } else if (targetNodes) {
33367 // snap to way - a line target with `.nodes`
33368 edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
33376 context.replace(actionMoveNode(entity.id, loc)); // Below here: validations
33378 var isInvalid = false; // Check if this connection to `target` could cause relations to break..
33381 isInvalid = hasRelationConflict(entity, target, edge, context.graph());
33382 } // Check if this drag causes the geometry to break..
33386 isInvalid = hasInvalidGeometry(entity, context.graph());
33389 var nope = context.surface().classed('nope');
33391 if (isInvalid === 'relation' || isInvalid === 'restriction') {
33393 // about to nope - show hint
33394 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.connect.' + isInvalid, {
33395 relation: _mainPresetIndex.item('type/restriction').name()
33398 } else if (isInvalid) {
33399 var errorID = isInvalid === 'line' ? 'lines' : 'areas';
33400 context.ui().flash.duration(3000).iconName('#iD-icon-no').label(_t('self_intersection.error.' + errorID))();
33403 // about to un-nope, remove hint
33404 context.ui().flash.duration(1).label('')();
33408 var nopeDisabled = context.surface().classed('nope-disabled');
33410 if (nopeDisabled) {
33411 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
33413 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
33417 } // Uses `actionConnect.disabled()` to know whether this connection is ok..
33420 function hasRelationConflict(entity, target, edge, graph) {
33421 var testGraph = graph.update(); // copy
33422 // if snapping to way - add midpoint there and consider that the target..
33425 var midpoint = osmNode();
33426 var action = actionAddMidpoint({
33428 edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
33430 testGraph = action(testGraph);
33432 } // can we connect to it?
33435 var ids = [entity.id, target.id];
33436 return actionConnect(ids).disabled(testGraph);
33439 function hasInvalidGeometry(entity, graph) {
33440 var parents = graph.parentWays(entity);
33443 for (i = 0; i < parents.length; i++) {
33444 var parent = parents[i];
33446 var activeIndex = null; // which multipolygon ring contains node being dragged
33447 // test any parent multipolygons for valid geometry
33449 var relations = graph.parentRelations(parent);
33451 for (j = 0; j < relations.length; j++) {
33452 if (!relations[j].isMultipolygon()) continue;
33453 var rings = osmJoinWays(relations[j].members, graph); // find active ring and test it for self intersections
33455 for (k = 0; k < rings.length; k++) {
33456 nodes = rings[k].nodes;
33458 if (nodes.find(function (n) {
33459 return n.id === entity.id;
33463 if (geoHasSelfIntersections(nodes, entity.id)) {
33464 return 'multipolygonMember';
33468 rings[k].coords = nodes.map(function (n) {
33471 } // test active ring for intersections with other rings in the multipolygon
33474 for (k = 0; k < rings.length; k++) {
33475 if (k === activeIndex) continue; // make sure active ring doesn't cross passive rings
33477 if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
33478 return 'multipolygonRing';
33481 } // If we still haven't tested this node's parent way for self-intersections.
33482 // (because it's not a member of a multipolygon), test it now.
33485 if (activeIndex === null) {
33486 nodes = parent.nodes.map(function (nodeID) {
33487 return graph.entity(nodeID);
33490 if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
33491 return parent.geometry(graph);
33499 function move(d3_event, entity, point) {
33500 if (_isCancelled) return;
33501 d3_event.stopPropagation();
33502 context.surface().classed('nope-disabled', d3_event.altKey);
33503 _lastLoc = context.projection.invert(point);
33504 doMove(d3_event, entity);
33505 var nudge = geoViewportEdge(point, context.map().dimensions());
33508 startNudge(d3_event, entity, nudge);
33514 function end(d3_event, entity) {
33515 if (_isCancelled) return;
33516 var wasPoint = entity.geometry(context.graph()) === 'point';
33517 var d = datum(d3_event);
33518 var nope = d && d.properties && d.properties.nope || context.surface().classed('nope');
33519 var target = d && d.properties && d.properties.entity; // entity to snap to
33523 context.perform(_actionBounceBack(entity.id, _startLoc));
33524 } else if (target && target.type === 'way') {
33525 var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
33526 context.replace(actionAddMidpoint({
33528 edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
33529 }, entity), connectAnnotation(entity, target));
33530 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
33531 context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target));
33532 } else if (_wasMidpoint) {
33533 context.replace(actionNoop(), _t('operations.add.annotation.vertex'));
33535 context.replace(actionNoop(), moveAnnotation(entity));
33539 context.enter(modeSelect(context, [entity.id]));
33541 var reselection = _restoreSelectedIDs.filter(function (id) {
33542 return context.graph().hasEntity(id);
33545 if (reselection.length) {
33546 context.enter(modeSelect(context, reselection));
33548 context.enter(modeBrowse(context));
33553 function _actionBounceBack(nodeID, toLoc) {
33554 var moveNode = actionMoveNode(nodeID, toLoc);
33556 var action = function action(graph, t) {
33557 // last time through, pop off the bounceback perform.
33558 // it will then overwrite the initial perform with a moveNode that does nothing
33559 if (t === 1) context.pop();
33560 return moveNode(graph, t);
33563 action.transitionable = true;
33567 function cancel() {
33569 context.enter(modeBrowse(context));
33572 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);
33574 mode.enter = function () {
33575 context.install(hover);
33576 context.install(edit);
33577 select(window).on('keydown.dragNode', keydown).on('keyup.dragNode', keyup);
33578 context.history().on('undone.drag-node', cancel);
33581 mode.exit = function () {
33582 context.ui().sidebar.hover.cancel();
33583 context.uninstall(hover);
33584 context.uninstall(edit);
33585 select(window).on('keydown.dragNode', null).on('keyup.dragNode', null);
33586 context.history().on('undone.drag-node', null);
33587 _activeEntity = null;
33588 context.surface().classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false).selectAll('.active').classed('active', false);
33592 mode.selectedIDs = function () {
33593 if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign
33598 mode.activeID = function () {
33599 if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign
33604 mode.restoreSelectedIDs = function (_) {
33605 if (!arguments.length) return _restoreSelectedIDs;
33606 _restoreSelectedIDs = _;
33610 mode.behavior = drag;
33614 // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829
33615 var NON_GENERIC = !!nativePromiseConstructor && fails(function () {
33616 nativePromiseConstructor.prototype['finally'].call({ then: function () { /* empty */ } }, function () { /* empty */ });
33619 // `Promise.prototype.finally` method
33620 // https://tc39.github.io/ecma262/#sec-promise.prototype.finally
33621 _export({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, {
33622 'finally': function (onFinally) {
33623 var C = speciesConstructor(this, getBuiltIn('Promise'));
33624 var isFunction = typeof onFinally == 'function';
33626 isFunction ? function (x) {
33627 return promiseResolve(C, onFinally()).then(function () { return x; });
33629 isFunction ? function (e) {
33630 return promiseResolve(C, onFinally()).then(function () { throw e; });
33636 // patch native Promise.prototype for native async functions
33637 if ( typeof nativePromiseConstructor == 'function' && !nativePromiseConstructor.prototype['finally']) {
33638 redefine(nativePromiseConstructor.prototype, 'finally', getBuiltIn('Promise').prototype['finally']);
33642 fixRegexpWellKnownSymbolLogic('search', 1, function (SEARCH, nativeSearch, maybeCallNative) {
33644 // `String.prototype.search` method
33645 // https://tc39.github.io/ecma262/#sec-string.prototype.search
33646 function search(regexp) {
33647 var O = requireObjectCoercible(this);
33648 var searcher = regexp == undefined ? undefined : regexp[SEARCH];
33649 return searcher !== undefined ? searcher.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
33651 // `RegExp.prototype[@@search]` method
33652 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@search
33653 function (regexp) {
33654 var res = maybeCallNative(nativeSearch, regexp, this);
33655 if (res.done) return res.value;
33657 var rx = anObject(regexp);
33658 var S = String(this);
33660 var previousLastIndex = rx.lastIndex;
33661 if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0;
33662 var result = regexpExecAbstract(rx, S);
33663 if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex;
33664 return result === null ? -1 : result.index;
33669 function quickselect$1(arr, k, left, right, compare) {
33670 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
33673 function quickselectStep(arr, k, left, right, compare) {
33674 while (right > left) {
33675 if (right - left > 600) {
33676 var n = right - left + 1;
33677 var m = k - left + 1;
33678 var z = Math.log(n);
33679 var s = 0.5 * Math.exp(2 * z / 3);
33680 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
33681 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
33682 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
33683 quickselectStep(arr, k, newLeft, newRight, compare);
33689 swap$1(arr, left, k);
33690 if (compare(arr[right], t) > 0) swap$1(arr, left, right);
33697 while (compare(arr[i], t) < 0) {
33701 while (compare(arr[j], t) > 0) {
33706 if (compare(arr[left], t) === 0) swap$1(arr, left, j);else {
33708 swap$1(arr, j, right);
33710 if (j <= k) left = j + 1;
33711 if (k <= j) right = j - 1;
33715 function swap$1(arr, i, j) {
33721 function defaultCompare(a, b) {
33722 return a < b ? -1 : a > b ? 1 : 0;
33725 var RBush = /*#__PURE__*/function () {
33727 var maxEntries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 9;
33729 _classCallCheck(this, RBush);
33731 // max entries in a node is 9 by default; min node fill is 40% for best performance
33732 this._maxEntries = Math.max(4, maxEntries);
33733 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
33737 _createClass(RBush, [{
33739 value: function all() {
33740 return this._all(this.data, []);
33744 value: function search(bbox) {
33745 var node = this.data;
33747 if (!intersects(bbox, node)) return result;
33748 var toBBox = this.toBBox;
33749 var nodesToSearch = [];
33752 for (var i = 0; i < node.children.length; i++) {
33753 var child = node.children[i];
33754 var childBBox = node.leaf ? toBBox(child) : child;
33756 if (intersects(bbox, childBBox)) {
33757 if (node.leaf) result.push(child);else if (contains(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
33761 node = nodesToSearch.pop();
33768 value: function collides(bbox) {
33769 var node = this.data;
33770 if (!intersects(bbox, node)) return false;
33771 var nodesToSearch = [];
33774 for (var i = 0; i < node.children.length; i++) {
33775 var child = node.children[i];
33776 var childBBox = node.leaf ? this.toBBox(child) : child;
33778 if (intersects(bbox, childBBox)) {
33779 if (node.leaf || contains(bbox, childBBox)) return true;
33780 nodesToSearch.push(child);
33784 node = nodesToSearch.pop();
33791 value: function load(data) {
33792 if (!(data && data.length)) return this;
33794 if (data.length < this._minEntries) {
33795 for (var i = 0; i < data.length; i++) {
33796 this.insert(data[i]);
33800 } // recursively build the tree with the given data from scratch using OMT algorithm
33803 var node = this._build(data.slice(), 0, data.length - 1, 0);
33805 if (!this.data.children.length) {
33806 // save as is if tree is empty
33808 } else if (this.data.height === node.height) {
33809 // split root if trees have the same height
33810 this._splitRoot(this.data, node);
33812 if (this.data.height < node.height) {
33813 // swap trees if inserted one is bigger
33814 var tmpNode = this.data;
33817 } // insert the small tree into the large tree at appropriate level
33820 this._insert(node, this.data.height - node.height - 1, true);
33827 value: function insert(item) {
33828 if (item) this._insert(item, this.data.height - 1);
33833 value: function clear() {
33834 this.data = createNode([]);
33839 value: function remove(item, equalsFn) {
33840 if (!item) return this;
33841 var node = this.data;
33842 var bbox = this.toBBox(item);
33845 var i, parent, goingUp; // depth-first iterative tree traversal
33847 while (node || path.length) {
33851 parent = path[path.length - 1];
33857 // check current node
33858 var index = findItem(item, node.children, equalsFn);
33860 if (index !== -1) {
33861 // item found, remove the item and condense tree upwards
33862 node.children.splice(index, 1);
33865 this._condense(path);
33871 if (!goingUp && !node.leaf && contains(node, bbox)) {
33877 node = node.children[0];
33878 } else if (parent) {
33881 node = parent.children[i];
33883 } else node = null; // nothing found
33891 value: function toBBox(item) {
33895 key: "compareMinX",
33896 value: function compareMinX(a, b) {
33897 return a.minX - b.minX;
33900 key: "compareMinY",
33901 value: function compareMinY(a, b) {
33902 return a.minY - b.minY;
33906 value: function toJSON() {
33911 value: function fromJSON(data) {
33917 value: function _all(node, result) {
33918 var nodesToSearch = [];
33921 if (node.leaf) result.push.apply(result, _toConsumableArray(node.children));else nodesToSearch.push.apply(nodesToSearch, _toConsumableArray(node.children));
33922 node = nodesToSearch.pop();
33929 value: function _build(items, left, right, height) {
33930 var N = right - left + 1;
33931 var M = this._maxEntries;
33935 // reached leaf level; return leaf
33936 node = createNode(items.slice(left, right + 1));
33937 calcBBox(node, this.toBBox);
33942 // target height of the bulk-loaded tree
33943 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
33945 M = Math.ceil(N / Math.pow(M, height - 1));
33948 node = createNode([]);
33950 node.height = height; // split the items into M mostly square tiles
33952 var N2 = Math.ceil(N / M);
33953 var N1 = N2 * Math.ceil(Math.sqrt(M));
33954 multiSelect(items, left, right, N1, this.compareMinX);
33956 for (var i = left; i <= right; i += N1) {
33957 var right2 = Math.min(i + N1 - 1, right);
33958 multiSelect(items, i, right2, N2, this.compareMinY);
33960 for (var j = i; j <= right2; j += N2) {
33961 var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
33963 node.children.push(this._build(items, j, right3, height - 1));
33967 calcBBox(node, this.toBBox);
33971 key: "_chooseSubtree",
33972 value: function _chooseSubtree(bbox, node, level, path) {
33975 if (node.leaf || path.length - 1 === level) break;
33976 var minArea = Infinity;
33977 var minEnlargement = Infinity;
33978 var targetNode = void 0;
33980 for (var i = 0; i < node.children.length; i++) {
33981 var child = node.children[i];
33982 var area = bboxArea(child);
33983 var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement
33985 if (enlargement < minEnlargement) {
33986 minEnlargement = enlargement;
33987 minArea = area < minArea ? area : minArea;
33988 targetNode = child;
33989 } else if (enlargement === minEnlargement) {
33990 // otherwise choose one with the smallest area
33991 if (area < minArea) {
33993 targetNode = child;
33998 node = targetNode || node.children[0];
34005 value: function _insert(item, level, isNode) {
34006 var bbox = isNode ? item : this.toBBox(item);
34007 var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
34009 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
34012 node.children.push(item);
34013 extend$1(node, bbox); // split on node overflow; propagate upwards if necessary
34015 while (level >= 0) {
34016 if (insertPath[level].children.length > this._maxEntries) {
34017 this._split(insertPath, level);
34021 } // adjust bboxes along the insertion path
34024 this._adjustParentBBoxes(bbox, insertPath, level);
34025 } // split overflowed node into two
34029 value: function _split(insertPath, level) {
34030 var node = insertPath[level];
34031 var M = node.children.length;
34032 var m = this._minEntries;
34034 this._chooseSplitAxis(node, m, M);
34036 var splitIndex = this._chooseSplitIndex(node, m, M);
34038 var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
34039 newNode.height = node.height;
34040 newNode.leaf = node.leaf;
34041 calcBBox(node, this.toBBox);
34042 calcBBox(newNode, this.toBBox);
34043 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
34047 value: function _splitRoot(node, newNode) {
34049 this.data = createNode([node, newNode]);
34050 this.data.height = node.height + 1;
34051 this.data.leaf = false;
34052 calcBBox(this.data, this.toBBox);
34055 key: "_chooseSplitIndex",
34056 value: function _chooseSplitIndex(node, m, M) {
34058 var minOverlap = Infinity;
34059 var minArea = Infinity;
34061 for (var i = m; i <= M - m; i++) {
34062 var bbox1 = distBBox(node, 0, i, this.toBBox);
34063 var bbox2 = distBBox(node, i, M, this.toBBox);
34064 var overlap = intersectionArea(bbox1, bbox2);
34065 var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap
34067 if (overlap < minOverlap) {
34068 minOverlap = overlap;
34070 minArea = area < minArea ? area : minArea;
34071 } else if (overlap === minOverlap) {
34072 // otherwise choose distribution with minimum area
34073 if (area < minArea) {
34080 return index || M - m;
34081 } // sorts node children by the best axis for split
34084 key: "_chooseSplitAxis",
34085 value: function _chooseSplitAxis(node, m, M) {
34086 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
34087 var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
34089 var xMargin = this._allDistMargin(node, m, M, compareMinX);
34091 var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
34092 // otherwise it's already sorted by minY
34095 if (xMargin < yMargin) node.children.sort(compareMinX);
34096 } // total margin of all possible split distributions where each node is at least m full
34099 key: "_allDistMargin",
34100 value: function _allDistMargin(node, m, M, compare) {
34101 node.children.sort(compare);
34102 var toBBox = this.toBBox;
34103 var leftBBox = distBBox(node, 0, m, toBBox);
34104 var rightBBox = distBBox(node, M - m, M, toBBox);
34105 var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
34107 for (var i = m; i < M - m; i++) {
34108 var child = node.children[i];
34109 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
34110 margin += bboxMargin(leftBBox);
34113 for (var _i = M - m - 1; _i >= m; _i--) {
34114 var _child = node.children[_i];
34115 extend$1(rightBBox, node.leaf ? toBBox(_child) : _child);
34116 margin += bboxMargin(rightBBox);
34122 key: "_adjustParentBBoxes",
34123 value: function _adjustParentBBoxes(bbox, path, level) {
34124 // adjust bboxes along the given tree path
34125 for (var i = level; i >= 0; i--) {
34126 extend$1(path[i], bbox);
34131 value: function _condense(path) {
34132 // go through the path, removing empty nodes and updating bboxes
34133 for (var i = path.length - 1, siblings; i >= 0; i--) {
34134 if (path[i].children.length === 0) {
34136 siblings = path[i - 1].children;
34137 siblings.splice(siblings.indexOf(path[i]), 1);
34138 } else this.clear();
34139 } else calcBBox(path[i], this.toBBox);
34147 function findItem(item, items, equalsFn) {
34148 if (!equalsFn) return items.indexOf(item);
34150 for (var i = 0; i < items.length; i++) {
34151 if (equalsFn(item, items[i])) return i;
34155 } // calculate node's bbox from bboxes of its children
34158 function calcBBox(node, toBBox) {
34159 distBBox(node, 0, node.children.length, toBBox, node);
34160 } // min bounding rectangle of node children from k to p-1
34163 function distBBox(node, k, p, toBBox, destNode) {
34164 if (!destNode) destNode = createNode(null);
34165 destNode.minX = Infinity;
34166 destNode.minY = Infinity;
34167 destNode.maxX = -Infinity;
34168 destNode.maxY = -Infinity;
34170 for (var i = k; i < p; i++) {
34171 var child = node.children[i];
34172 extend$1(destNode, node.leaf ? toBBox(child) : child);
34178 function extend$1(a, b) {
34179 a.minX = Math.min(a.minX, b.minX);
34180 a.minY = Math.min(a.minY, b.minY);
34181 a.maxX = Math.max(a.maxX, b.maxX);
34182 a.maxY = Math.max(a.maxY, b.maxY);
34186 function compareNodeMinX(a, b) {
34187 return a.minX - b.minX;
34190 function compareNodeMinY(a, b) {
34191 return a.minY - b.minY;
34194 function bboxArea(a) {
34195 return (a.maxX - a.minX) * (a.maxY - a.minY);
34198 function bboxMargin(a) {
34199 return a.maxX - a.minX + (a.maxY - a.minY);
34202 function enlargedArea(a, b) {
34203 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));
34206 function intersectionArea(a, b) {
34207 var minX = Math.max(a.minX, b.minX);
34208 var minY = Math.max(a.minY, b.minY);
34209 var maxX = Math.min(a.maxX, b.maxX);
34210 var maxY = Math.min(a.maxY, b.maxY);
34211 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
34214 function contains(a, b) {
34215 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
34218 function intersects(a, b) {
34219 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
34222 function createNode(children) {
34224 children: children,
34232 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
34233 // combines selection algorithm with binary divide & conquer approach
34236 function multiSelect(arr, left, right, n, compare) {
34237 var stack = [left, right];
34239 while (stack.length) {
34240 right = stack.pop();
34241 left = stack.pop();
34242 if (right - left <= n) continue;
34243 var mid = left + Math.ceil((right - left) / n / 2) * n;
34244 quickselect$1(arr, mid, left, right, compare);
34245 stack.push(left, mid, mid, right);
34249 var tiler = utilTiler();
34250 var dispatch$1 = dispatch('loaded');
34251 var _tileZoom = 14;
34252 var _krUrlRoot = 'https://www.keepright.at';
34255 localizeStrings: {}
34256 }; // This gets reassigned if reset
34260 var _krRuleset = [// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
34261 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];
34263 function abortRequest(controller) {
34265 controller.abort();
34269 function abortUnwantedRequests(cache, tiles) {
34270 Object.keys(cache.inflightTile).forEach(function (k) {
34271 var wanted = tiles.find(function (tile) {
34272 return k === tile.id;
34276 abortRequest(cache.inflightTile[k]);
34277 delete cache.inflightTile[k];
34282 function encodeIssueRtree(d) {
34290 } // Replace or remove QAItem from rtree
34293 function updateRtree(item, replace) {
34294 _cache.rtree.remove(item, function (a, b) {
34295 return a.data.id === b.data.id;
34299 _cache.rtree.insert(item);
34303 function tokenReplacements(d) {
34304 if (!(d instanceof QAItem)) return;
34305 var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
34306 var replacements = {};
34307 var issueTemplate = _krData.errorTypes[d.whichType];
34309 if (!issueTemplate) {
34310 /* eslint-disable no-console */
34311 console.log('No Template: ', d.whichType);
34312 console.log(' ', d.description);
34313 /* eslint-enable no-console */
34316 } // some descriptions are just fixed text
34319 if (!issueTemplate.regex) return; // regex pattern should match description with variable details captured
34321 var errorRegex = new RegExp(issueTemplate.regex, 'i');
34322 var errorMatch = errorRegex.exec(d.description);
34325 /* eslint-disable no-console */
34326 console.log('Unmatched: ', d.whichType);
34327 console.log(' ', d.description);
34328 console.log(' ', errorRegex);
34329 /* eslint-enable no-console */
34334 for (var i = 1; i < errorMatch.length; i++) {
34336 var capture = errorMatch[i];
34337 var idType = void 0;
34338 idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i - 1] : '';
34340 if (idType && capture) {
34341 // link IDs if present in the capture
34342 capture = parseError(capture, idType);
34343 } else if (htmlRegex.test(capture)) {
34344 // escape any html in non-IDs
34345 capture = '\\' + capture + '\\';
34347 var compare = capture.toLowerCase();
34349 if (_krData.localizeStrings[compare]) {
34350 // some replacement strings can be localized
34351 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
34355 replacements['var' + i] = capture;
34358 return replacements;
34361 function parseError(capture, idType) {
34362 var compare = capture.toLowerCase();
34364 if (_krData.localizeStrings[compare]) {
34365 // some replacement strings can be localized
34366 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
34370 // link a string like "this node"
34372 capture = linkErrorObject(capture);
34376 capture = linkURL(capture);
34378 // link an entity ID
34383 capture = linkEntity(idType + capture);
34385 // some errors have more complex ID lists/variance
34388 capture = parse20(capture);
34392 capture = parse211(capture);
34396 capture = parse231(capture);
34400 capture = parse294(capture);
34404 capture = parse370(capture);
34410 function linkErrorObject(d) {
34411 return "<a class=\"error_object_link\">".concat(d, "</a>");
34414 function linkEntity(d) {
34415 return "<a class=\"error_entity_link\">".concat(d, "</a>");
34418 function linkURL(d) {
34419 return "<a class=\"kr_external_link\" target=\"_blank\" href=\"".concat(d, "\">").concat(d, "</a>");
34420 } // arbitrary node list of form: #ID, #ID, #ID...
34423 function parse211(capture) {
34425 var items = capture.split(', ');
34426 items.forEach(function (item) {
34427 // ID has # at the front
34428 var id = linkEntity('n' + item.slice(1));
34431 return newList.join(', ');
34432 } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
34435 function parse231(capture) {
34436 var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),'
34438 var items = capture.split('),');
34439 items.forEach(function (item) {
34440 var match = item.match(/\#(\d+)\((.+)\)?/);
34442 if (match !== null && match.length > 2) {
34443 newList.push(linkEntity('w' + match[1]) + ' ' + _t('QA.keepRight.errorTypes.231.layer', {
34448 return newList.join(', ');
34449 } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
34452 function parse294(capture) {
34454 var items = capture.split(',');
34455 items.forEach(function (item) {
34456 // item of form "from/to node/relation #ID"
34457 item = item.split(' '); // to/from role is more clear in quotes
34459 var role = "\"".concat(item[0], "\""); // first letter of node/relation provides the type
34461 var idType = item[1].slice(0, 1); // ID has # at the front
34463 var id = item[2].slice(1);
34464 id = linkEntity(idType + id);
34465 newList.push("".concat(role, " ").concat(item[1], " ").concat(id));
34467 return newList.join(', ');
34468 } // may or may not include the string "(including the name 'name')"
34471 function parse370(capture) {
34472 if (!capture) return '';
34473 var match = capture.match(/\(including the name (\'.+\')\)/);
34475 if (match && match.length) {
34476 return _t('QA.keepRight.errorTypes.370.including_the_name', {
34482 } // arbitrary node list of form: #ID,#ID,#ID...
34485 function parse20(capture) {
34487 var items = capture.split(',');
34488 items.forEach(function (item) {
34489 // ID has # at the front
34490 var id = linkEntity('n' + item.slice(1));
34493 return newList.join(', ');
34497 var serviceKeepRight = {
34498 title: 'keepRight',
34499 init: function init() {
34500 _mainFileFetcher.get('keepRight').then(function (d) {
34501 return _krData = d;
34508 this.event = utilRebind(this, dispatch$1, 'on');
34510 reset: function reset() {
34512 Object.values(_cache.inflightTile).forEach(abortRequest);
34524 // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php
34525 loadIssues: function loadIssues(projection) {
34531 }; // determine the needed tiles to cover the view
34533 var tiles = tiler.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection); // abort inflight requests that are no longer needed
34535 abortUnwantedRequests(_cache, tiles); // issue new requests..
34537 tiles.forEach(function (tile) {
34538 if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
34540 var _tile$extent$rectangl = tile.extent.rectangle(),
34541 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
34542 left = _tile$extent$rectangl2[0],
34543 top = _tile$extent$rectangl2[1],
34544 right = _tile$extent$rectangl2[2],
34545 bottom = _tile$extent$rectangl2[3];
34547 var params = Object.assign({}, options, {
34553 var url = "".concat(_krUrlRoot, "/export.php?") + utilQsString(params);
34554 var controller = new AbortController();
34555 _cache.inflightTile[tile.id] = controller;
34557 signal: controller.signal
34558 }).then(function (data) {
34559 delete _cache.inflightTile[tile.id];
34560 _cache.loadedTile[tile.id] = true;
34562 if (!data || !data.features || !data.features.length) {
34563 throw new Error('No Data');
34566 data.features.forEach(function (feature) {
34567 var _feature$properties = feature.properties,
34568 itemType = _feature$properties.error_type,
34569 id = _feature$properties.error_id,
34570 _feature$properties$c = _feature$properties.comment,
34571 comment = _feature$properties$c === void 0 ? null : _feature$properties$c,
34572 objectId = _feature$properties.object_id,
34573 objectType = _feature$properties.object_type,
34574 schema = _feature$properties.schema,
34575 title = _feature$properties.title;
34576 var loc = feature.geometry.coordinates,
34577 _feature$properties$d = feature.properties.description,
34578 description = _feature$properties$d === void 0 ? '' : _feature$properties$d; // if there is a parent, save its error type e.g.:
34579 // Error 191 = "highway-highway"
34580 // Error 190 = "intersections without junctions" (parent)
34582 var issueTemplate = _krData.errorTypes[itemType];
34583 var parentIssueType = (Math.floor(itemType / 10) * 10).toString(); // try to handle error type directly, fallback to parent error type.
34585 var whichType = issueTemplate ? itemType : parentIssueType;
34586 var whichTemplate = _krData.errorTypes[whichType]; // Rewrite a few of the errors at this point..
34587 // This is done to make them easier to linkify and translate.
34589 switch (whichType) {
34591 description = "This feature has a FIXME tag: ".concat(description);
34596 description = description.replace('A turn-', 'This turn-');
34604 description = "This turn-restriction~".concat(description);
34608 description = 'This highway is missing a maxspeed tag';
34614 description = "This feature~".concat(description);
34616 } // move markers slightly so it doesn't obscure the geometry,
34617 // then move markers away from other coincident markers
34620 var coincident = false;
34623 // first time, move marker up. after that, move marker right.
34624 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
34625 loc = geoVecAdd(loc, delta);
34626 var bbox = geoExtent(loc).bbox();
34627 coincident = _cache.rtree.search(bbox).length;
34628 } while (coincident);
34630 var d = new QAItem(loc, _this, itemType, id, {
34632 description: description,
34633 whichType: whichType,
34634 parentIssueType: parentIssueType,
34635 severity: whichTemplate.severity || 'error',
34636 objectId: objectId,
34637 objectType: objectType,
34641 d.replacements = tokenReplacements(d);
34642 _cache.data[id] = d;
34644 _cache.rtree.insert(encodeIssueRtree(d));
34646 dispatch$1.call('loaded');
34647 })["catch"](function () {
34648 delete _cache.inflightTile[tile.id];
34649 _cache.loadedTile[tile.id] = true;
34653 postUpdate: function postUpdate(d, callback) {
34656 if (_cache.inflightPost[d.id]) {
34658 message: 'Error update already inflight',
34669 params.st = d.newStatus;
34672 if (d.newComment !== undefined) {
34673 params.co = d.newComment;
34674 } // NOTE: This throws a CORS err, but it seems successful.
34675 // We don't care too much about the response, so this is fine.
34678 var url = "".concat(_krUrlRoot, "/comment.php?") + utilQsString(params);
34679 var controller = new AbortController();
34680 _cache.inflightPost[d.id] = controller; // Since this is expected to throw an error just continue as if it worked
34681 // (worst case scenario the request truly fails and issue will show up if iD restarts)
34684 signal: controller.signal
34685 })["finally"](function () {
34686 delete _cache.inflightPost[d.id];
34688 if (d.newStatus === 'ignore') {
34689 // ignore permanently (false positive)
34690 _this2.removeItem(d);
34691 } else if (d.newStatus === 'ignore_t') {
34692 // ignore temporarily (error fixed)
34693 _this2.removeItem(d);
34695 _cache.closed["".concat(d.schema, ":").concat(d.id)] = true;
34697 d = _this2.replaceItem(d.update({
34698 comment: d.newComment,
34699 newComment: undefined,
34700 newState: undefined
34704 if (callback) callback(null, d);
34707 // Get all cached QAItems covering the viewport
34708 getItems: function getItems(projection) {
34709 var viewport = projection.clipExtent();
34710 var min = [viewport[0][0], viewport[1][1]];
34711 var max = [viewport[1][0], viewport[0][1]];
34712 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34713 return _cache.rtree.search(bbox).map(function (d) {
34717 // Get a QAItem from cache
34718 // NOTE: Don't change method name until UI v3 is merged
34719 getError: function getError(id) {
34720 return _cache.data[id];
34722 // Replace a single QAItem in the cache
34723 replaceItem: function replaceItem(item) {
34724 if (!(item instanceof QAItem) || !item.id) return;
34725 _cache.data[item.id] = item;
34726 updateRtree(encodeIssueRtree(item), true); // true = replace
34730 // Remove a single QAItem from the cache
34731 removeItem: function removeItem(item) {
34732 if (!(item instanceof QAItem) || !item.id) return;
34733 delete _cache.data[item.id];
34734 updateRtree(encodeIssueRtree(item), false); // false = remove
34736 issueURL: function issueURL(item) {
34737 return "".concat(_krUrlRoot, "/report_map.php?schema=").concat(item.schema, "&error=").concat(item.id);
34739 // Get an array of issues closed during this session.
34740 // Used to populate `closed:keepright` changeset tag
34741 getClosedIDs: function getClosedIDs() {
34742 return Object.keys(_cache.closed).sort();
34746 var tiler$1 = utilTiler();
34747 var dispatch$2 = dispatch('loaded');
34748 var _tileZoom$1 = 14;
34749 var _impOsmUrls = {
34750 ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
34751 mr: 'https://grab.community.improve-osm.org/missingGeoService',
34752 tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
34754 var _impOsmData = {
34756 }; // This gets reassigned if reset
34760 function abortRequest$1(i) {
34761 Object.values(i).forEach(function (controller) {
34763 controller.abort();
34768 function abortUnwantedRequests$1(cache, tiles) {
34769 Object.keys(cache.inflightTile).forEach(function (k) {
34770 var wanted = tiles.find(function (tile) {
34771 return k === tile.id;
34775 abortRequest$1(cache.inflightTile[k]);
34776 delete cache.inflightTile[k];
34781 function encodeIssueRtree$1(d) {
34789 } // Replace or remove QAItem from rtree
34792 function updateRtree$1(item, replace) {
34793 _cache$1.rtree.remove(item, function (a, b) {
34794 return a.data.id === b.data.id;
34798 _cache$1.rtree.insert(item);
34802 function linkErrorObject(d) {
34803 return "<a class=\"error_object_link\">".concat(d, "</a>");
34806 function linkEntity(d) {
34807 return "<a class=\"error_entity_link\">".concat(d, "</a>");
34810 function pointAverage(points) {
34811 if (points.length) {
34812 var sum = points.reduce(function (acc, point) {
34813 return geoVecAdd(acc, [point.lon, point.lat]);
34815 return geoVecScale(sum, 1 / points.length);
34821 function relativeBearing(p1, p2) {
34822 var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
34825 angle += 2 * Math.PI;
34826 } // Return degrees
34829 return angle * 180 / Math.PI;
34830 } // Assuming range [0,360)
34833 function cardinalDirection(bearing) {
34834 var dir = 45 * Math.round(bearing / 45);
34846 return _t("QA.improveOSM.directions.".concat(compass[dir]));
34847 } // Errors shouldn't obscure each other
34850 function preventCoincident(loc, bumpUp) {
34851 var coincident = false;
34854 // first time, move marker up. after that, move marker right.
34855 var delta = coincident ? [0.00001, 0] : bumpUp ? [0, 0.00001] : [0, 0];
34856 loc = geoVecAdd(loc, delta);
34857 var bbox = geoExtent(loc).bbox();
34858 coincident = _cache$1.rtree.search(bbox).length;
34859 } while (coincident);
34864 var serviceImproveOSM = {
34865 title: 'improveOSM',
34866 init: function init() {
34867 _mainFileFetcher.get('qa_data').then(function (d) {
34868 return _impOsmData = d.improveOSM;
34875 this.event = utilRebind(this, dispatch$2, 'on');
34877 reset: function reset() {
34879 Object.values(_cache$1.inflightTile).forEach(abortRequest$1);
34891 loadIssues: function loadIssues(projection) {
34897 zoom: '19' // Use a high zoom so that clusters aren't returned
34899 }; // determine the needed tiles to cover the view
34901 var tiles = tiler$1.zoomExtent([_tileZoom$1, _tileZoom$1]).getTiles(projection); // abort inflight requests that are no longer needed
34903 abortUnwantedRequests$1(_cache$1, tiles); // issue new requests..
34905 tiles.forEach(function (tile) {
34906 if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
34908 var _tile$extent$rectangl = tile.extent.rectangle(),
34909 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
34910 east = _tile$extent$rectangl2[0],
34911 north = _tile$extent$rectangl2[1],
34912 west = _tile$extent$rectangl2[2],
34913 south = _tile$extent$rectangl2[3];
34915 var params = Object.assign({}, options, {
34920 }); // 3 separate requests to store for each tile
34923 Object.keys(_impOsmUrls).forEach(function (k) {
34924 // We exclude WATER from missing geometry as it doesn't seem useful
34925 // We use most confident one-way and turn restrictions only, still have false positives
34926 var kParams = Object.assign({}, params, k === 'mr' ? {
34927 type: 'PARKING,ROAD,BOTH,PATH'
34929 confidenceLevel: 'C1'
34931 var url = "".concat(_impOsmUrls[k], "/search?") + utilQsString(kParams);
34932 var controller = new AbortController();
34933 requests[k] = controller;
34935 signal: controller.signal
34936 }).then(function (data) {
34937 delete _cache$1.inflightTile[tile.id][k];
34939 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
34940 delete _cache$1.inflightTile[tile.id];
34941 _cache$1.loadedTile[tile.id] = true;
34942 } // Road segments at high zoom == oneways
34945 if (data.roadSegments) {
34946 data.roadSegments.forEach(function (feature) {
34947 // Position error at the approximate middle of the segment
34948 var points = feature.points,
34949 wayId = feature.wayId,
34950 fromNodeId = feature.fromNodeId,
34951 toNodeId = feature.toNodeId;
34952 var itemId = "".concat(wayId).concat(fromNodeId).concat(toNodeId);
34953 var mid = points.length / 2;
34954 var loc; // Even number of points, find midpoint of the middle two
34955 // Odd number of points, use position of very middle point
34957 if (mid % 1 === 0) {
34958 loc = pointAverage([points[mid - 1], points[mid]]);
34960 mid = points[Math.floor(mid)];
34961 loc = [mid.lon, mid.lat];
34962 } // One-ways can land on same segment in opposite direction
34965 loc = preventCoincident(loc, false);
34966 var d = new QAItem(loc, _this, k, itemId, {
34968 // used as a category
34970 // used to post changes
34972 fromNodeId: fromNodeId,
34977 }); // Variables used in the description
34980 percentage: feature.percentOfTrips,
34981 num_trips: feature.numberOfTrips,
34982 highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
34983 from_node: linkEntity('n' + feature.fromNodeId),
34984 to_node: linkEntity('n' + feature.toNodeId)
34986 _cache$1.data[d.id] = d;
34988 _cache$1.rtree.insert(encodeIssueRtree$1(d));
34990 } // Tiles at high zoom == missing roads
34994 data.tiles.forEach(function (feature) {
34995 var type = feature.type,
34998 numberOfTrips = feature.numberOfTrips;
34999 var geoType = type.toLowerCase();
35000 var itemId = "".concat(geoType).concat(x).concat(y).concat(numberOfTrips); // Average of recorded points should land on the missing geometry
35001 // Missing geometry could happen to land on another error
35003 var loc = pointAverage(feature.points);
35004 loc = preventCoincident(loc, false);
35005 var d = new QAItem(loc, _this, "".concat(k, "-").concat(geoType), itemId, {
35013 num_trips: numberOfTrips,
35014 geometry_type: _t("QA.improveOSM.geometry_types.".concat(geoType))
35015 }; // -1 trips indicates data came from a 3rd party
35017 if (numberOfTrips === -1) {
35018 d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
35021 _cache$1.data[d.id] = d;
35023 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35025 } // Entities at high zoom == turn restrictions
35028 if (data.entities) {
35029 data.entities.forEach(function (feature) {
35030 var point = feature.point,
35032 segments = feature.segments,
35033 numberOfPasses = feature.numberOfPasses,
35034 turnType = feature.turnType;
35035 var itemId = "".concat(id.replace(/[,:+#]/g, '_')); // Turn restrictions could be missing at same junction
35036 // We also want to bump the error up so node is accessible
35038 var loc = preventCoincident([point.lon, point.lat], true); // Elements are presented in a strange way
35040 var ids = id.split(',');
35041 var from_way = ids[0];
35042 var via_node = ids[3];
35043 var to_way = ids[2].split(':')[1];
35044 var d = new QAItem(loc, _this, k, itemId, {
35047 objectId: via_node,
35049 }); // Travel direction along from_way clarifies the turn restriction
35051 var _segments$0$points = _slicedToArray(segments[0].points, 2),
35052 p1 = _segments$0$points[0],
35053 p2 = _segments$0$points[1];
35055 var dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); // Variables used in the description
35058 num_passed: numberOfPasses,
35059 num_trips: segments[0].numberOfTrips,
35060 turn_restriction: turnType.toLowerCase(),
35061 from_way: linkEntity('w' + from_way),
35062 to_way: linkEntity('w' + to_way),
35063 travel_direction: dir_of_travel,
35064 junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
35066 _cache$1.data[d.id] = d;
35068 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35070 dispatch$2.call('loaded');
35073 })["catch"](function () {
35074 delete _cache$1.inflightTile[tile.id][k];
35076 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
35077 delete _cache$1.inflightTile[tile.id];
35078 _cache$1.loadedTile[tile.id] = true;
35082 _cache$1.inflightTile[tile.id] = requests;
35085 getComments: function getComments(item) {
35088 // If comments already retrieved no need to do so again
35089 if (item.comments) {
35090 return Promise.resolve(item);
35093 var key = item.issueKey;
35096 if (key === 'ow') {
35097 qParams = item.identifier;
35098 } else if (key === 'mr') {
35099 qParams.tileX = item.identifier.x;
35100 qParams.tileY = item.identifier.y;
35101 } else if (key === 'tr') {
35102 qParams.targetId = item.identifier;
35105 var url = "".concat(_impOsmUrls[key], "/retrieveComments?") + utilQsString(qParams);
35107 var cacheComments = function cacheComments(data) {
35108 // Assign directly for immediate use afterwards
35109 // comments are served newest to oldest
35110 item.comments = data.comments ? data.comments.reverse() : [];
35112 _this2.replaceItem(item);
35115 return d3_json(url).then(cacheComments).then(function () {
35119 postUpdate: function postUpdate(d, callback) {
35120 if (!serviceOsm.authenticated()) {
35121 // Username required in payload
35123 message: 'Not Authenticated',
35128 if (_cache$1.inflightPost[d.id]) {
35130 message: 'Error update already inflight',
35133 } // Payload can only be sent once username is established
35136 serviceOsm.userDetails(sendPayload.bind(this));
35138 function sendPayload(err, user) {
35142 return callback(err, d);
35145 var key = d.issueKey;
35146 var url = "".concat(_impOsmUrls[key], "/comment");
35148 username: user.display_name,
35149 targetIds: [d.identifier]
35153 payload.status = d.newStatus;
35154 payload.text = 'status changed';
35155 } // Comment take place of default text
35158 if (d.newComment) {
35159 payload.text = d.newComment;
35162 var controller = new AbortController();
35163 _cache$1.inflightPost[d.id] = controller;
35166 signal: controller.signal,
35167 body: JSON.stringify(payload)
35169 d3_json(url, options).then(function () {
35170 delete _cache$1.inflightPost[d.id]; // Just a comment, update error in cache
35172 if (!d.newStatus) {
35173 var now = new Date();
35174 var comments = d.comments ? d.comments : [];
35176 username: payload.username,
35177 text: payload.text,
35178 timestamp: now.getTime() / 1000
35181 _this3.replaceItem(d.update({
35182 comments: comments,
35183 newComment: undefined
35186 _this3.removeItem(d);
35188 if (d.newStatus === 'SOLVED') {
35189 // Keep track of the number of issues closed per type to tag the changeset
35190 if (!(d.issueKey in _cache$1.closed)) {
35191 _cache$1.closed[d.issueKey] = 0;
35194 _cache$1.closed[d.issueKey] += 1;
35198 if (callback) callback(null, d);
35199 })["catch"](function (err) {
35200 delete _cache$1.inflightPost[d.id];
35201 if (callback) callback(err.message);
35205 // Get all cached QAItems covering the viewport
35206 getItems: function getItems(projection) {
35207 var viewport = projection.clipExtent();
35208 var min = [viewport[0][0], viewport[1][1]];
35209 var max = [viewport[1][0], viewport[0][1]];
35210 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
35211 return _cache$1.rtree.search(bbox).map(function (d) {
35215 // Get a QAItem from cache
35216 // NOTE: Don't change method name until UI v3 is merged
35217 getError: function getError(id) {
35218 return _cache$1.data[id];
35220 // get the name of the icon to display for this item
35221 getIcon: function getIcon(itemType) {
35222 return _impOsmData.icons[itemType];
35224 // Replace a single QAItem in the cache
35225 replaceItem: function replaceItem(issue) {
35226 if (!(issue instanceof QAItem) || !issue.id) return;
35227 _cache$1.data[issue.id] = issue;
35228 updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace
35232 // Remove a single QAItem from the cache
35233 removeItem: function removeItem(issue) {
35234 if (!(issue instanceof QAItem) || !issue.id) return;
35235 delete _cache$1.data[issue.id];
35236 updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove
35238 // Used to populate `closed:improveosm:*` changeset tags
35239 getClosedCounts: function getClosedCounts() {
35240 return _cache$1.closed;
35246 // B.2.3.2.1 CreateHTML(string, tag, attribute, value)
35247 // https://tc39.github.io/ecma262/#sec-createhtml
35248 var createHtml = function (string, tag, attribute, value) {
35249 var S = String(requireObjectCoercible(string));
35250 var p1 = '<' + tag;
35251 if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '"') + '"';
35252 return p1 + '>' + S + '</' + tag + '>';
35255 // check the existence of a method, lowercase
35256 // of a tag and escaping quotes in arguments
35257 var stringHtmlForced = function (METHOD_NAME) {
35258 return fails(function () {
35259 var test = ''[METHOD_NAME]('"');
35260 return test !== test.toLowerCase() || test.split('"').length > 3;
35264 // `String.prototype.link` method
35265 // https://tc39.github.io/ecma262/#sec-string.prototype.link
35266 _export({ target: 'String', proto: true, forced: stringHtmlForced('link') }, {
35267 link: function link(url) {
35268 return createHtml(this, 'a', 'href', url);
35272 var getOwnPropertyDescriptor$4 = objectGetOwnPropertyDescriptor.f;
35279 var nativeEndsWith = ''.endsWith;
35280 var min$8 = Math.min;
35282 var CORRECT_IS_REGEXP_LOGIC = correctIsRegexpLogic('endsWith');
35283 // https://github.com/zloirock/core-js/pull/702
35284 var MDN_POLYFILL_BUG = !CORRECT_IS_REGEXP_LOGIC && !!function () {
35285 var descriptor = getOwnPropertyDescriptor$4(String.prototype, 'endsWith');
35286 return descriptor && !descriptor.writable;
35289 // `String.prototype.endsWith` method
35290 // https://tc39.github.io/ecma262/#sec-string.prototype.endswith
35291 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {
35292 endsWith: function endsWith(searchString /* , endPosition = @length */) {
35293 var that = String(requireObjectCoercible(this));
35294 notARegexp(searchString);
35295 var endPosition = arguments.length > 1 ? arguments[1] : undefined;
35296 var len = toLength(that.length);
35297 var end = endPosition === undefined ? len : min$8(toLength(endPosition), len);
35298 var search = String(searchString);
35299 return nativeEndsWith
35300 ? nativeEndsWith.call(that, search, end)
35301 : that.slice(end - search.length, end) === search;
35305 var getOwnPropertyDescriptor$5 = objectGetOwnPropertyDescriptor.f;
35312 var nativeStartsWith = ''.startsWith;
35313 var min$9 = Math.min;
35315 var CORRECT_IS_REGEXP_LOGIC$1 = correctIsRegexpLogic('startsWith');
35316 // https://github.com/zloirock/core-js/pull/702
35317 var MDN_POLYFILL_BUG$1 = !CORRECT_IS_REGEXP_LOGIC$1 && !!function () {
35318 var descriptor = getOwnPropertyDescriptor$5(String.prototype, 'startsWith');
35319 return descriptor && !descriptor.writable;
35322 // `String.prototype.startsWith` method
35323 // https://tc39.github.io/ecma262/#sec-string.prototype.startswith
35324 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG$1 && !CORRECT_IS_REGEXP_LOGIC$1 }, {
35325 startsWith: function startsWith(searchString /* , position = 0 */) {
35326 var that = String(requireObjectCoercible(this));
35327 notARegexp(searchString);
35328 var index = toLength(min$9(arguments.length > 1 ? arguments[1] : undefined, that.length));
35329 var search = String(searchString);
35330 return nativeStartsWith
35331 ? nativeStartsWith.call(that, search, index)
35332 : that.slice(index, index + search.length) === search;
35336 var $trimEnd = stringTrim.end;
35339 var FORCED$e = stringTrimForced('trimEnd');
35341 var trimEnd = FORCED$e ? function trimEnd() {
35342 return $trimEnd(this);
35345 // `String.prototype.{ trimEnd, trimRight }` methods
35346 // https://github.com/tc39/ecmascript-string-left-right-trim
35347 _export({ target: 'String', proto: true, forced: FORCED$e }, {
35352 var defaults = createCommonjsModule(function (module) {
35353 function getDefaults() {
35361 langPrefix: 'language-',
35369 smartypants: false,
35376 function changeDefaults(newDefaults) {
35377 module.exports.defaults = newDefaults;
35381 defaults: getDefaults(),
35382 getDefaults: getDefaults,
35383 changeDefaults: changeDefaults
35390 var escapeTest = /[&<>"']/;
35391 var escapeReplace = /[&<>"']/g;
35392 var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
35393 var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
35394 var escapeReplacements = {
35402 var getEscapeReplacement = function getEscapeReplacement(ch) {
35403 return escapeReplacements[ch];
35406 function escape$1(html, encode) {
35408 if (escapeTest.test(html)) {
35409 return html.replace(escapeReplace, getEscapeReplacement);
35412 if (escapeTestNoEncode.test(html)) {
35413 return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
35420 var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
35422 function unescape$1(html) {
35423 // explicitly match decimal, hex, and named HTML entities
35424 return html.replace(unescapeTest, function (_, n) {
35425 n = n.toLowerCase();
35426 if (n === 'colon') return ':';
35428 if (n.charAt(0) === '#') {
35429 return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));
35436 var caret = /(^|[^\[])\^/g;
35438 function edit(regex, opt) {
35439 regex = regex.source || regex;
35442 replace: function replace(name, val) {
35443 val = val.source || val;
35444 val = val.replace(caret, '$1');
35445 regex = regex.replace(name, val);
35448 getRegex: function getRegex() {
35449 return new RegExp(regex, opt);
35455 var nonWordAndColonTest = /[^\w:]/g;
35456 var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
35458 function cleanUrl(sanitize, base, href) {
35463 prot = decodeURIComponent(unescape$1(href)).replace(nonWordAndColonTest, '').toLowerCase();
35468 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
35473 if (base && !originIndependentUrl.test(href)) {
35474 href = resolveUrl(base, href);
35478 href = encodeURI(href).replace(/%25/g, '%');
35487 var justDomain = /^[^:]+:\/*[^/]*$/;
35488 var protocol = /^([^:]+:)[\s\S]*$/;
35489 var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
35491 function resolveUrl(base, href) {
35492 if (!baseUrls[' ' + base]) {
35493 // we can ignore everything in base after the last slash of its path component,
35494 // but we might need to add _that_
35495 // https://tools.ietf.org/html/rfc3986#section-3
35496 if (justDomain.test(base)) {
35497 baseUrls[' ' + base] = base + '/';
35499 baseUrls[' ' + base] = rtrim$1(base, '/', true);
35503 base = baseUrls[' ' + base];
35504 var relativeBase = base.indexOf(':') === -1;
35506 if (href.substring(0, 2) === '//') {
35507 if (relativeBase) {
35511 return base.replace(protocol, '$1') + href;
35512 } else if (href.charAt(0) === '/') {
35513 if (relativeBase) {
35517 return base.replace(domain, '$1') + href;
35519 return base + href;
35524 exec: function noopTest() {}
35527 function merge$1(obj) {
35532 for (; i < arguments.length; i++) {
35533 target = arguments[i];
35535 for (key in target) {
35536 if (Object.prototype.hasOwnProperty.call(target, key)) {
35537 obj[key] = target[key];
35545 function splitCells(tableRow, count) {
35546 // ensure that every cell-delimiting pipe has a space
35547 // before it to distinguish it from an escaped pipe
35548 var row = tableRow.replace(/\|/g, function (match, offset, str) {
35549 var escaped = false,
35552 while (--curr >= 0 && str[curr] === '\\') {
35553 escaped = !escaped;
35557 // odd number of slashes means | is escaped
35558 // so we leave it alone
35561 // add space before unescaped |
35565 cells = row.split(/ \|/);
35568 if (cells.length > count) {
35569 cells.splice(count);
35571 while (cells.length < count) {
35576 for (; i < cells.length; i++) {
35577 // leading or trailing whitespace is ignored per the gfm spec
35578 cells[i] = cells[i].trim().replace(/\\\|/g, '|');
35582 } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
35583 // /c*$/ is vulnerable to REDOS.
35584 // invert: Remove suffix of non-c chars instead. Default falsey.
35587 function rtrim$1(str, c, invert) {
35588 var l = str.length;
35592 } // Length of suffix matching the invert condition.
35595 var suffLen = 0; // Step left until we fail to match the invert condition.
35597 while (suffLen < l) {
35598 var currChar = str.charAt(l - suffLen - 1);
35600 if (currChar === c && !invert) {
35602 } else if (currChar !== c && invert) {
35609 return str.substr(0, l - suffLen);
35612 function findClosingBracket(str, b) {
35613 if (str.indexOf(b[1]) === -1) {
35617 var l = str.length;
35621 for (; i < l; i++) {
35622 if (str[i] === '\\') {
35624 } else if (str[i] === b[0]) {
35626 } else if (str[i] === b[1]) {
35638 function checkSanitizeDeprecation(opt) {
35639 if (opt && opt.sanitize && !opt.silent) {
35640 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');
35642 } // copied from https://stackoverflow.com/a/5450113/806777
35645 function repeatString(pattern, count) {
35652 while (count > 1) {
35658 pattern += pattern;
35661 return result + pattern;
35666 unescape: unescape$1,
35668 cleanUrl: cleanUrl,
35669 resolveUrl: resolveUrl,
35670 noopTest: noopTest,
35672 splitCells: splitCells,
35674 findClosingBracket: findClosingBracket,
35675 checkSanitizeDeprecation: checkSanitizeDeprecation,
35676 repeatString: repeatString
35679 var defaults$1 = defaults.defaults;
35680 var rtrim$2 = helpers.rtrim,
35681 splitCells$1 = helpers.splitCells,
35682 _escape = helpers.escape,
35683 findClosingBracket$1 = helpers.findClosingBracket;
35685 function outputLink(cap, link, raw) {
35686 var href = link.href;
35687 var title = link.title ? _escape(link.title) : null;
35688 var text = cap[1].replace(/\\([\[\]])/g, '$1');
35690 if (cap[0].charAt(0) !== '!') {
35704 text: _escape(text)
35709 function indentCodeCompensation(raw, text) {
35710 var matchIndentToCode = raw.match(/^(\s+)(?:```)/);
35712 if (matchIndentToCode === null) {
35716 var indentToCode = matchIndentToCode[1];
35717 return text.split('\n').map(function (node) {
35718 var matchIndentInNode = node.match(/^\s+/);
35720 if (matchIndentInNode === null) {
35724 var _matchIndentInNode = _slicedToArray(matchIndentInNode, 1),
35725 indentInNode = _matchIndentInNode[0];
35727 if (indentInNode.length >= indentToCode.length) {
35728 return node.slice(indentToCode.length);
35739 var Tokenizer_1 = /*#__PURE__*/function () {
35740 function Tokenizer(options) {
35741 _classCallCheck(this, Tokenizer);
35743 this.options = options || defaults$1;
35746 _createClass(Tokenizer, [{
35748 value: function space(src) {
35749 var cap = this.rules.block.newline.exec(src);
35752 if (cap[0].length > 1) {
35766 value: function code(src, tokens) {
35767 var cap = this.rules.block.code.exec(src);
35770 var lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph.
35772 if (lastToken && lastToken.type === 'paragraph') {
35775 text: cap[0].trimRight()
35779 var text = cap[0].replace(/^ {4}/gm, '');
35783 codeBlockStyle: 'indented',
35784 text: !this.options.pedantic ? rtrim$2(text, '\n') : text
35790 value: function fences(src) {
35791 var cap = this.rules.block.fences.exec(src);
35795 var text = indentCodeCompensation(raw, cap[3] || '');
35799 lang: cap[2] ? cap[2].trim() : cap[2],
35806 value: function heading(src) {
35807 var cap = this.rules.block.heading.exec(src);
35813 depth: cap[1].length,
35820 value: function nptable(src) {
35821 var cap = this.rules.block.nptable.exec(src);
35826 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
35827 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
35828 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
35832 if (item.header.length === item.align.length) {
35833 var l = item.align.length;
35836 for (i = 0; i < l; i++) {
35837 if (/^ *-+: *$/.test(item.align[i])) {
35838 item.align[i] = 'right';
35839 } else if (/^ *:-+: *$/.test(item.align[i])) {
35840 item.align[i] = 'center';
35841 } else if (/^ *:-+ *$/.test(item.align[i])) {
35842 item.align[i] = 'left';
35844 item.align[i] = null;
35848 l = item.cells.length;
35850 for (i = 0; i < l; i++) {
35851 item.cells[i] = splitCells$1(item.cells[i], item.header.length);
35860 value: function hr(src) {
35861 var cap = this.rules.block.hr.exec(src);
35872 value: function blockquote(src) {
35873 var cap = this.rules.block.blockquote.exec(src);
35876 var text = cap[0].replace(/^ *> ?/gm, '');
35878 type: 'blockquote',
35886 value: function list(src) {
35887 var cap = this.rules.block.list.exec(src);
35892 var isordered = bull.length > 1;
35893 var isparen = bull[bull.length - 1] === ')';
35897 ordered: isordered,
35898 start: isordered ? +bull.slice(0, -1) : '',
35901 }; // Get each top-level item.
35903 var itemMatch = cap[0].match(this.rules.block.item);
35912 var l = itemMatch.length;
35914 for (var i = 0; i < l; i++) {
35915 item = itemMatch[i];
35916 raw = item; // Remove the list item's bullet
35917 // so it is seen as the next token.
35919 space = item.length;
35920 item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the
35921 // list item contains. Hacky.
35923 if (~item.indexOf('\n ')) {
35924 space -= item.length;
35925 item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
35926 } // Determine whether the next list item belongs here.
35927 // Backpedal if it does not belong in this list.
35931 b = this.rules.block.bullet.exec(itemMatch[i + 1])[0];
35933 if (isordered ? b.length === 1 || !isparen && b[b.length - 1] === ')' : b.length > 1 || this.options.smartLists && b !== bull) {
35934 addBack = itemMatch.slice(i + 1).join('\n');
35935 list.raw = list.raw.substring(0, list.raw.length - addBack.length);
35938 } // Determine whether item is loose or not.
35939 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
35940 // for discount behavior.
35943 loose = next || /\n\n(?!\s*$)/.test(item);
35946 next = item.charAt(item.length - 1) === '\n';
35947 if (!loose) loose = next;
35952 } // Check for task list items
35955 istask = /^\[[ xX]\] /.test(item);
35956 ischecked = undefined;
35959 ischecked = item[1] !== ' ';
35960 item = item.replace(/^\[[ xX]\] +/, '');
35967 checked: ischecked,
35978 value: function html(src) {
35979 var cap = this.rules.block.html.exec(src);
35983 type: this.options.sanitize ? 'paragraph' : 'html',
35985 pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
35986 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
35992 value: function def(src) {
35993 var cap = this.rules.block.def.exec(src);
35996 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
35997 var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
36008 value: function table(src) {
36009 var cap = this.rules.block.table.exec(src);
36014 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
36015 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
36016 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
36019 if (item.header.length === item.align.length) {
36021 var l = item.align.length;
36024 for (i = 0; i < l; i++) {
36025 if (/^ *-+: *$/.test(item.align[i])) {
36026 item.align[i] = 'right';
36027 } else if (/^ *:-+: *$/.test(item.align[i])) {
36028 item.align[i] = 'center';
36029 } else if (/^ *:-+ *$/.test(item.align[i])) {
36030 item.align[i] = 'left';
36032 item.align[i] = null;
36036 l = item.cells.length;
36038 for (i = 0; i < l; i++) {
36039 item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length);
36048 value: function lheading(src) {
36049 var cap = this.rules.block.lheading.exec(src);
36055 depth: cap[2].charAt(0) === '=' ? 1 : 2,
36062 value: function paragraph(src) {
36063 var cap = this.rules.block.paragraph.exec(src);
36069 text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
36075 value: function text(src, tokens) {
36076 var cap = this.rules.block.text.exec(src);
36079 var lastToken = tokens[tokens.length - 1];
36081 if (lastToken && lastToken.type === 'text') {
36097 value: function escape(src) {
36098 var cap = this.rules.inline.escape.exec(src);
36104 text: _escape(cap[1])
36110 value: function tag(src, inLink, inRawBlock) {
36111 var cap = this.rules.inline.tag.exec(src);
36114 if (!inLink && /^<a /i.test(cap[0])) {
36116 } else if (inLink && /^<\/a>/i.test(cap[0])) {
36120 if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
36122 } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
36123 inRawBlock = false;
36127 type: this.options.sanitize ? 'text' : 'html',
36130 inRawBlock: inRawBlock,
36131 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
36137 value: function link(src) {
36138 var cap = this.rules.inline.link.exec(src);
36141 var lastParenIndex = findClosingBracket$1(cap[2], '()');
36143 if (lastParenIndex > -1) {
36144 var start = cap[0].indexOf('!') === 0 ? 5 : 4;
36145 var linkLen = start + cap[1].length + lastParenIndex;
36146 cap[2] = cap[2].substring(0, lastParenIndex);
36147 cap[0] = cap[0].substring(0, linkLen).trim();
36154 if (this.options.pedantic) {
36155 var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
36164 title = cap[3] ? cap[3].slice(1, -1) : '';
36167 href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
36168 var token = outputLink(cap, {
36169 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
36170 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
36177 value: function reflink(src, links) {
36180 if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
36181 var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
36182 link = links[link.toLowerCase()];
36184 if (!link || !link.href) {
36185 var text = cap[0].charAt(0);
36193 var token = outputLink(cap, link, cap[0]);
36199 value: function strong(src, maskedSrc) {
36200 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
36201 var match = this.rules.inline.strong.start.exec(src);
36203 if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
36204 maskedSrc = maskedSrc.slice(-1 * src.length);
36205 var endReg = match[0] === '**' ? this.rules.inline.strong.endAst : this.rules.inline.strong.endUnd;
36206 endReg.lastIndex = 0;
36209 while ((match = endReg.exec(maskedSrc)) != null) {
36210 cap = this.rules.inline.strong.middle.exec(maskedSrc.slice(0, match.index + 3));
36215 raw: src.slice(0, cap[0].length),
36216 text: src.slice(2, cap[0].length - 2)
36224 value: function em(src, maskedSrc) {
36225 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
36226 var match = this.rules.inline.em.start.exec(src);
36228 if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
36229 maskedSrc = maskedSrc.slice(-1 * src.length);
36230 var endReg = match[0] === '*' ? this.rules.inline.em.endAst : this.rules.inline.em.endUnd;
36231 endReg.lastIndex = 0;
36234 while ((match = endReg.exec(maskedSrc)) != null) {
36235 cap = this.rules.inline.em.middle.exec(maskedSrc.slice(0, match.index + 2));
36240 raw: src.slice(0, cap[0].length),
36241 text: src.slice(1, cap[0].length - 1)
36249 value: function codespan(src) {
36250 var cap = this.rules.inline.code.exec(src);
36253 var text = cap[2].replace(/\n/g, ' ');
36254 var hasNonSpaceChars = /[^ ]/.test(text);
36255 var hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' ');
36257 if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
36258 text = text.substring(1, text.length - 1);
36261 text = _escape(text, true);
36271 value: function br(src) {
36272 var cap = this.rules.inline.br.exec(src);
36283 value: function del(src) {
36284 var cap = this.rules.inline.del.exec(src);
36296 value: function autolink(src, mangle) {
36297 var cap = this.rules.inline.autolink.exec(src);
36302 if (cap[2] === '@') {
36303 text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
36304 href = 'mailto:' + text;
36306 text = _escape(cap[1]);
36325 value: function url(src, mangle) {
36328 if (cap = this.rules.inline.url.exec(src)) {
36331 if (cap[2] === '@') {
36332 text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
36333 href = 'mailto:' + text;
36335 // do extended autolink path validation
36339 prevCapZero = cap[0];
36340 cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
36341 } while (prevCapZero !== cap[0]);
36343 text = _escape(cap[0]);
36345 if (cap[1] === 'www.') {
36346 href = 'http://' + text;
36367 value: function inlineText(src, inRawBlock, smartypants) {
36368 var cap = this.rules.inline.text.exec(src);
36374 text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0];
36376 text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
36391 var noopTest$1 = helpers.noopTest,
36392 edit$1 = helpers.edit,
36393 merge$2 = helpers.merge;
36395 * Block-Level Grammar
36400 code: /^( {4}[^\n]+\n*)+/,
36401 fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
36402 hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
36403 heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
36404 blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
36405 list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
36406 html: '^ {0,3}(?:' // optional indentation
36407 + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
36408 + '|comment[^\\n]*(\\n+|$)' // (2)
36409 + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
36410 + '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
36411 + '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
36412 + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
36413 + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
36414 + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
36416 def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
36417 nptable: noopTest$1,
36419 lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
36420 // regex template, placeholders will be replaced according to different paragraph
36421 // interruption rules of commonmark and the original markdown spec:
36422 _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
36425 block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
36426 block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
36427 block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex();
36428 block.bullet = /(?:[*+-]|\d{1,9}[.)])/;
36429 block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
36430 block.item = edit$1(block.item, 'gm').replace(/bull/g, block.bullet).getRegex();
36431 block.list = edit$1(block.list).replace(/bull/g, block.bullet).replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))').replace('def', '\\n+(?=' + block.def.source + ')').getRegex();
36432 block._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';
36433 block._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
36434 block.html = edit$1(block.html, 'i').replace('comment', block._comment).replace('tag', block._tag).replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
36435 block.paragraph = edit$1(block._paragraph).replace('hr', block.hr).replace('heading', ' {0,3}#{1,6} ').replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
36436 .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
36437 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
36439 block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex();
36441 * Normal Block Grammar
36444 block.normal = merge$2({}, block);
36446 * GFM Block Grammar
36449 block.gfm = merge$2({}, block.normal, {
36450 nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
36451 + ' {0,3}([-:]+ *\\|[-| :]*)' // Align
36452 + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)',
36454 table: '^ *\\|(.+)\\n' // Header
36455 + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align
36456 + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
36459 block.gfm.nptable = edit$1(block.gfm.nptable).replace('hr', block.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
36460 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
36462 block.gfm.table = edit$1(block.gfm.table).replace('hr', block.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
36463 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
36466 * Pedantic grammar (original John Gruber's loose markdown specification)
36469 block.pedantic = merge$2({}, block.normal, {
36470 html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
36471 + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))').replace('comment', block._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(),
36472 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
36473 heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
36474 fences: noopTest$1,
36475 // fences not supported
36476 paragraph: edit$1(block.normal._paragraph).replace('hr', block.hr).replace('heading', ' *#{1,6} *[^\n]').replace('lheading', block.lheading).replace('blockquote', ' {0,3}>').replace('|fences', '').replace('|list', '').replace('|html', '').getRegex()
36479 * Inline-Level Grammar
36483 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
36484 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
36486 tag: '^comment' + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
36487 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
36488 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
36489 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
36490 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
36492 link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
36493 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
36494 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
36495 reflinkSearch: 'reflink|nolink(?!\\()',
36497 start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,
36498 // (1) returns if starts w/ punctuation
36499 middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,
36500 endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,
36501 // last char can't be punct, or final * must also be followed by punct (or endline)
36502 endUnd: /[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)
36506 start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,
36507 // (1) returns if starts w/ punctuation
36508 middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,
36509 endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,
36510 // last char can't be punct, or final * must also be followed by punct (or endline)
36511 endUnd: /[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)
36514 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
36515 br: /^( {2,}|\\)\n(?!\s*$)/,
36517 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n)))/,
36518 punctuation: /^([\s*punctuation])/
36519 }; // list of punctuation marks from common mark spec
36520 // without * and _ to workaround cases with double emphasis
36522 inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~';
36523 inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, <html>
36525 inline._blockSkip = '\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>';
36526 inline._overlapSkip = '__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*';
36527 inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex();
36528 inline.em.start = edit$1(inline.em.start).replace(/punctuation/g, inline._punctuation).getRegex();
36529 inline.em.middle = edit$1(inline.em.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
36530 inline.em.endAst = edit$1(inline.em.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36531 inline.em.endUnd = edit$1(inline.em.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36532 inline.strong.start = edit$1(inline.strong.start).replace(/punctuation/g, inline._punctuation).getRegex();
36533 inline.strong.middle = edit$1(inline.strong.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
36534 inline.strong.endAst = edit$1(inline.strong.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36535 inline.strong.endUnd = edit$1(inline.strong.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36536 inline.blockSkip = edit$1(inline._blockSkip, 'g').getRegex();
36537 inline.overlapSkip = edit$1(inline._overlapSkip, 'g').getRegex();
36538 inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
36539 inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
36540 inline._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])?)+(?![-_])/;
36541 inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex();
36542 inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
36543 inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex();
36544 inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
36545 inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
36546 inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
36547 inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex();
36548 inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex();
36549 inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex();
36551 * Normal Inline Grammar
36554 inline.normal = merge$2({}, inline);
36556 * Pedantic Inline Grammar
36559 inline.pedantic = merge$2({}, inline.normal, {
36562 middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
36563 endAst: /\*\*(?!\*)/g,
36568 middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
36569 endAst: /\*(?!\*)/g,
36572 link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(),
36573 reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex()
36576 * GFM Inline Grammar
36579 inline.gfm = merge$2({}, inline.normal, {
36580 escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
36581 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
36582 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
36583 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
36584 del: /^~+(?=\S)([\s\S]*?\S)~+/,
36585 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
36587 inline.gfm.url = edit$1(inline.gfm.url, 'i').replace('email', inline.gfm._extended_email).getRegex();
36589 * GFM + Line Breaks Inline Grammar
36592 inline.breaks = merge$2({}, inline.gfm, {
36593 br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
36594 text: edit$1(inline.gfm.text).replace('\\b_', '\\b_| {2,}\\n').replace(/\{2,\}/g, '*').getRegex()
36601 var defaults$2 = defaults.defaults;
36602 var block$1 = rules.block,
36603 inline$1 = rules.inline;
36604 var repeatString$1 = helpers.repeatString;
36606 * smartypants text replacement
36609 function smartypants(text) {
36610 return text // em-dashes
36611 .replace(/---/g, "\u2014") // en-dashes
36612 .replace(/--/g, "\u2013") // opening singles
36613 .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") // closing singles & apostrophes
36614 .replace(/'/g, "\u2019") // opening doubles
36615 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201C") // closing doubles
36616 .replace(/"/g, "\u201D") // ellipses
36617 .replace(/\.{3}/g, "\u2026");
36620 * mangle email addresses
36624 function mangle(text) {
36628 var l = text.length;
36630 for (i = 0; i < l; i++) {
36631 ch = text.charCodeAt(i);
36633 if (Math.random() > 0.5) {
36634 ch = 'x' + ch.toString(16);
36637 out += '&#' + ch + ';';
36647 var Lexer_1 = /*#__PURE__*/function () {
36648 function Lexer(options) {
36649 _classCallCheck(this, Lexer);
36652 this.tokens.links = Object.create(null);
36653 this.options = options || defaults$2;
36654 this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
36655 this.tokenizer = this.options.tokenizer;
36656 this.tokenizer.options = this.options;
36658 block: block$1.normal,
36659 inline: inline$1.normal
36662 if (this.options.pedantic) {
36663 rules.block = block$1.pedantic;
36664 rules.inline = inline$1.pedantic;
36665 } else if (this.options.gfm) {
36666 rules.block = block$1.gfm;
36668 if (this.options.breaks) {
36669 rules.inline = inline$1.breaks;
36671 rules.inline = inline$1.gfm;
36675 this.tokenizer.rules = rules;
36682 _createClass(Lexer, [{
36688 value: function lex(src) {
36689 src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' ');
36690 this.blockTokens(src, this.tokens, true);
36691 this.inline(this.tokens);
36692 return this.tokens;
36699 key: "blockTokens",
36700 value: function blockTokens(src) {
36701 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
36702 var top = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
36703 src = src.replace(/^ +$/gm, '');
36704 var token, i, l, lastToken;
36708 if (token = this.tokenizer.space(src)) {
36709 src = src.substring(token.raw.length);
36712 tokens.push(token);
36719 if (token = this.tokenizer.code(src, tokens)) {
36720 src = src.substring(token.raw.length);
36723 tokens.push(token);
36725 lastToken = tokens[tokens.length - 1];
36726 lastToken.raw += '\n' + token.raw;
36727 lastToken.text += '\n' + token.text;
36734 if (token = this.tokenizer.fences(src)) {
36735 src = src.substring(token.raw.length);
36736 tokens.push(token);
36741 if (token = this.tokenizer.heading(src)) {
36742 src = src.substring(token.raw.length);
36743 tokens.push(token);
36745 } // table no leading pipe (gfm)
36748 if (token = this.tokenizer.nptable(src)) {
36749 src = src.substring(token.raw.length);
36750 tokens.push(token);
36755 if (token = this.tokenizer.hr(src)) {
36756 src = src.substring(token.raw.length);
36757 tokens.push(token);
36762 if (token = this.tokenizer.blockquote(src)) {
36763 src = src.substring(token.raw.length);
36764 token.tokens = this.blockTokens(token.text, [], top);
36765 tokens.push(token);
36770 if (token = this.tokenizer.list(src)) {
36771 src = src.substring(token.raw.length);
36772 l = token.items.length;
36774 for (i = 0; i < l; i++) {
36775 token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
36778 tokens.push(token);
36783 if (token = this.tokenizer.html(src)) {
36784 src = src.substring(token.raw.length);
36785 tokens.push(token);
36790 if (top && (token = this.tokenizer.def(src))) {
36791 src = src.substring(token.raw.length);
36793 if (!this.tokens.links[token.tag]) {
36794 this.tokens.links[token.tag] = {
36804 if (token = this.tokenizer.table(src)) {
36805 src = src.substring(token.raw.length);
36806 tokens.push(token);
36811 if (token = this.tokenizer.lheading(src)) {
36812 src = src.substring(token.raw.length);
36813 tokens.push(token);
36815 } // top-level paragraph
36818 if (top && (token = this.tokenizer.paragraph(src))) {
36819 src = src.substring(token.raw.length);
36820 tokens.push(token);
36825 if (token = this.tokenizer.text(src, tokens)) {
36826 src = src.substring(token.raw.length);
36829 tokens.push(token);
36831 lastToken = tokens[tokens.length - 1];
36832 lastToken.raw += '\n' + token.raw;
36833 lastToken.text += '\n' + token.text;
36840 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
36842 if (this.options.silent) {
36843 console.error(errMsg);
36846 throw new Error(errMsg);
36855 value: function inline(tokens) {
36856 var i, j, k, l2, row, token;
36857 var l = tokens.length;
36859 for (i = 0; i < l; i++) {
36862 switch (token.type) {
36868 this.inlineTokens(token.text, token.tokens);
36879 l2 = token.header.length;
36881 for (j = 0; j < l2; j++) {
36882 token.tokens.header[j] = [];
36883 this.inlineTokens(token.header[j], token.tokens.header[j]);
36887 l2 = token.cells.length;
36889 for (j = 0; j < l2; j++) {
36890 row = token.cells[j];
36891 token.tokens.cells[j] = [];
36893 for (k = 0; k < row.length; k++) {
36894 token.tokens.cells[j][k] = [];
36895 this.inlineTokens(row[k], token.tokens.cells[j][k]);
36904 this.inline(token.tokens);
36910 l2 = token.items.length;
36912 for (j = 0; j < l2; j++) {
36913 this.inline(token.items[j].tokens);
36928 key: "inlineTokens",
36929 value: function inlineTokens(src) {
36930 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
36931 var inLink = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
36932 var inRawBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
36933 var prevChar = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '';
36934 var token; // String with links masked to avoid interference with em and strong
36936 var maskedSrc = src;
36937 var match; // Mask out reflinks
36939 if (this.tokens.links) {
36940 var links = Object.keys(this.tokens.links);
36942 if (links.length > 0) {
36943 while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
36944 if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
36945 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
36949 } // Mask out other blocks
36952 while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
36953 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
36958 if (token = this.tokenizer.escape(src)) {
36959 src = src.substring(token.raw.length);
36960 tokens.push(token);
36965 if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
36966 src = src.substring(token.raw.length);
36967 inLink = token.inLink;
36968 inRawBlock = token.inRawBlock;
36969 tokens.push(token);
36974 if (token = this.tokenizer.link(src)) {
36975 src = src.substring(token.raw.length);
36977 if (token.type === 'link') {
36978 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
36981 tokens.push(token);
36983 } // reflink, nolink
36986 if (token = this.tokenizer.reflink(src, this.tokens.links)) {
36987 src = src.substring(token.raw.length);
36989 if (token.type === 'link') {
36990 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
36993 tokens.push(token);
36998 if (token = this.tokenizer.strong(src, maskedSrc, prevChar)) {
36999 src = src.substring(token.raw.length);
37000 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37001 tokens.push(token);
37006 if (token = this.tokenizer.em(src, maskedSrc, prevChar)) {
37007 src = src.substring(token.raw.length);
37008 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37009 tokens.push(token);
37014 if (token = this.tokenizer.codespan(src)) {
37015 src = src.substring(token.raw.length);
37016 tokens.push(token);
37021 if (token = this.tokenizer.br(src)) {
37022 src = src.substring(token.raw.length);
37023 tokens.push(token);
37028 if (token = this.tokenizer.del(src)) {
37029 src = src.substring(token.raw.length);
37030 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37031 tokens.push(token);
37036 if (token = this.tokenizer.autolink(src, mangle)) {
37037 src = src.substring(token.raw.length);
37038 tokens.push(token);
37043 if (!inLink && (token = this.tokenizer.url(src, mangle))) {
37044 src = src.substring(token.raw.length);
37045 tokens.push(token);
37050 if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
37051 src = src.substring(token.raw.length);
37052 prevChar = token.raw.slice(-1);
37053 tokens.push(token);
37058 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
37060 if (this.options.silent) {
37061 console.error(errMsg);
37064 throw new Error(errMsg);
37075 * Static Lex Method
37077 value: function lex(src, options) {
37078 var lexer = new Lexer(options);
37079 return lexer.lex(src);
37082 * Static Lex Inline Method
37087 value: function lexInline(src, options) {
37088 var lexer = new Lexer(options);
37089 return lexer.inlineTokens(src);
37093 get: function get() {
37104 var defaults$3 = defaults.defaults;
37105 var cleanUrl$1 = helpers.cleanUrl,
37106 escape$2 = helpers.escape;
37111 var Renderer_1 = /*#__PURE__*/function () {
37112 function Renderer(options) {
37113 _classCallCheck(this, Renderer);
37115 this.options = options || defaults$3;
37118 _createClass(Renderer, [{
37120 value: function code(_code, infostring, escaped) {
37121 var lang = (infostring || '').match(/\S*/)[0];
37123 if (this.options.highlight) {
37124 var out = this.options.highlight(_code, lang);
37126 if (out != null && out !== _code) {
37133 return '<pre><code>' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
37136 return '<pre><code class="' + this.options.langPrefix + escape$2(lang, true) + '">' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
37140 value: function blockquote(quote) {
37141 return '<blockquote>\n' + quote + '</blockquote>\n';
37145 value: function html(_html) {
37150 value: function heading(text, level, raw, slugger) {
37151 if (this.options.headerIds) {
37152 return '<h' + level + ' id="' + this.options.headerPrefix + slugger.slug(raw) + '">' + text + '</h' + level + '>\n';
37156 return '<h' + level + '>' + text + '</h' + level + '>\n';
37160 value: function hr() {
37161 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
37165 value: function list(body, ordered, start) {
37166 var type = ordered ? 'ol' : 'ul',
37167 startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
37168 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
37172 value: function listitem(text) {
37173 return '<li>' + text + '</li>\n';
37177 value: function checkbox(checked) {
37178 return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + (this.options.xhtml ? ' /' : '') + '> ';
37182 value: function paragraph(text) {
37183 return '<p>' + text + '</p>\n';
37187 value: function table(header, body) {
37188 if (body) body = '<tbody>' + body + '</tbody>';
37189 return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
37193 value: function tablerow(content) {
37194 return '<tr>\n' + content + '</tr>\n';
37198 value: function tablecell(content, flags) {
37199 var type = flags.header ? 'th' : 'td';
37200 var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
37201 return tag + content + '</' + type + '>\n';
37202 } // span level renderer
37206 value: function strong(text) {
37207 return '<strong>' + text + '</strong>';
37211 value: function em(text) {
37212 return '<em>' + text + '</em>';
37216 value: function codespan(text) {
37217 return '<code>' + text + '</code>';
37221 value: function br() {
37222 return this.options.xhtml ? '<br/>' : '<br>';
37226 value: function del(text) {
37227 return '<del>' + text + '</del>';
37231 value: function link(href, title, text) {
37232 href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
37234 if (href === null) {
37238 var out = '<a href="' + escape$2(href) + '"';
37241 out += ' title="' + title + '"';
37244 out += '>' + text + '</a>';
37249 value: function image(href, title, text) {
37250 href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
37252 if (href === null) {
37256 var out = '<img src="' + href + '" alt="' + text + '"';
37259 out += ' title="' + title + '"';
37262 out += this.options.xhtml ? '/>' : '>';
37267 value: function text(_text) {
37277 * returns only the textual part of the token
37279 var TextRenderer_1 = /*#__PURE__*/function () {
37280 function TextRenderer() {
37281 _classCallCheck(this, TextRenderer);
37284 _createClass(TextRenderer, [{
37286 // no need for block level renderers
37287 value: function strong(text) {
37292 value: function em(text) {
37297 value: function codespan(text) {
37302 value: function del(text) {
37307 value: function html(text) {
37312 value: function text(_text) {
37317 value: function link(href, title, text) {
37322 value: function image(href, title, text) {
37327 value: function br() {
37332 return TextRenderer;
37336 * Slugger generates header id
37338 var Slugger_1 = /*#__PURE__*/function () {
37339 function Slugger() {
37340 _classCallCheck(this, Slugger);
37345 _createClass(Slugger, [{
37347 value: function serialize(value) {
37348 return value.toLowerCase().trim() // remove html tags
37349 .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars
37350 .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-');
37353 * Finds the next safe (unique) slug to use
37357 key: "getNextSafeSlug",
37358 value: function getNextSafeSlug(originalSlug, isDryRun) {
37359 var slug = originalSlug;
37360 var occurenceAccumulator = 0;
37362 if (this.seen.hasOwnProperty(slug)) {
37363 occurenceAccumulator = this.seen[originalSlug];
37366 occurenceAccumulator++;
37367 slug = originalSlug + '-' + occurenceAccumulator;
37368 } while (this.seen.hasOwnProperty(slug));
37372 this.seen[originalSlug] = occurenceAccumulator;
37373 this.seen[slug] = 0;
37379 * Convert string to unique id
37380 * @param {object} options
37381 * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator.
37386 value: function slug(value) {
37387 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
37388 var slug = this.serialize(value);
37389 return this.getNextSafeSlug(slug, options.dryrun);
37396 var defaults$4 = defaults.defaults;
37397 var unescape$2 = helpers.unescape;
37399 * Parsing & Compiling
37402 var Parser_1 = /*#__PURE__*/function () {
37403 function Parser(options) {
37404 _classCallCheck(this, Parser);
37406 this.options = options || defaults$4;
37407 this.options.renderer = this.options.renderer || new Renderer_1();
37408 this.renderer = this.options.renderer;
37409 this.renderer.options = this.options;
37410 this.textRenderer = new TextRenderer_1();
37411 this.slugger = new Slugger_1();
37414 * Static Parse Method
37418 _createClass(Parser, [{
37424 value: function parse(tokens) {
37425 var top = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
37445 var l = tokens.length;
37447 for (i = 0; i < l; i++) {
37450 switch (token.type) {
37458 out += this.renderer.hr();
37464 out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$2(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
37470 out += this.renderer.code(token.text, token.lang, token.escaped);
37476 header = ''; // header
37479 l2 = token.header.length;
37481 for (j = 0; j < l2; j++) {
37482 cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), {
37484 align: token.align[j]
37488 header += this.renderer.tablerow(cell);
37490 l2 = token.cells.length;
37492 for (j = 0; j < l2; j++) {
37493 row = token.tokens.cells[j];
37497 for (k = 0; k < l3; k++) {
37498 cell += this.renderer.tablecell(this.parseInline(row[k]), {
37500 align: token.align[k]
37504 body += this.renderer.tablerow(cell);
37507 out += this.renderer.table(header, body);
37513 body = this.parse(token.tokens);
37514 out += this.renderer.blockquote(body);
37520 ordered = token.ordered;
37521 start = token.start;
37522 loose = token.loose;
37523 l2 = token.items.length;
37526 for (j = 0; j < l2; j++) {
37527 item = token.items[j];
37528 checked = item.checked;
37533 checkbox = this.renderer.checkbox(checked);
37536 if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
37537 item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
37539 if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
37540 item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
37543 item.tokens.unshift({
37549 itemBody += checkbox;
37553 itemBody += this.parse(item.tokens, loose);
37554 body += this.renderer.listitem(itemBody, task, checked);
37557 out += this.renderer.list(body, ordered, start);
37563 // TODO parse inline content if parameter markdown=1
37564 out += this.renderer.html(token.text);
37570 out += this.renderer.paragraph(this.parseInline(token.tokens));
37576 body = token.tokens ? this.parseInline(token.tokens) : token.text;
37578 while (i + 1 < l && tokens[i + 1].type === 'text') {
37579 token = tokens[++i];
37580 body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
37583 out += top ? this.renderer.paragraph(body) : body;
37589 var errMsg = 'Token with "' + token.type + '" type was not found.';
37591 if (this.options.silent) {
37592 console.error(errMsg);
37595 throw new Error(errMsg);
37604 * Parse Inline Tokens
37608 key: "parseInline",
37609 value: function parseInline(tokens, renderer) {
37610 renderer = renderer || this.renderer;
37614 var l = tokens.length;
37616 for (i = 0; i < l; i++) {
37619 switch (token.type) {
37622 out += renderer.text(token.text);
37628 out += renderer.html(token.text);
37634 out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
37640 out += renderer.image(token.href, token.title, token.text);
37646 out += renderer.strong(this.parseInline(token.tokens, renderer));
37652 out += renderer.em(this.parseInline(token.tokens, renderer));
37658 out += renderer.codespan(token.text);
37664 out += renderer.br();
37670 out += renderer.del(this.parseInline(token.tokens, renderer));
37676 out += renderer.text(token.text);
37682 var errMsg = 'Token with "' + token.type + '" type was not found.';
37684 if (this.options.silent) {
37685 console.error(errMsg);
37688 throw new Error(errMsg);
37698 value: function parse(tokens, options) {
37699 var parser = new Parser(options);
37700 return parser.parse(tokens);
37703 * Static Parse Inline Method
37707 key: "parseInline",
37708 value: function parseInline(tokens, options) {
37709 var parser = new Parser(options);
37710 return parser.parseInline(tokens);
37717 var merge$3 = helpers.merge,
37718 checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation,
37719 escape$3 = helpers.escape;
37720 var getDefaults = defaults.getDefaults,
37721 changeDefaults = defaults.changeDefaults,
37722 defaults$5 = defaults.defaults;
37727 function marked(src, opt, callback) {
37728 // throw error in case of non string input
37729 if (typeof src === 'undefined' || src === null) {
37730 throw new Error('marked(): input parameter is undefined or null');
37733 if (typeof src !== 'string') {
37734 throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
37737 if (typeof opt === 'function') {
37742 opt = merge$3({}, marked.defaults, opt || {});
37743 checkSanitizeDeprecation$1(opt);
37746 var highlight = opt.highlight;
37750 tokens = Lexer_1.lex(src, opt);
37752 return callback(e);
37755 var done = function done(err) {
37760 out = Parser_1.parse(tokens, opt);
37766 opt.highlight = highlight;
37767 return err ? callback(err) : callback(null, out);
37770 if (!highlight || highlight.length < 3) {
37774 delete opt.highlight;
37775 if (!tokens.length) return done();
37777 marked.walkTokens(tokens, function (token) {
37778 if (token.type === 'code') {
37780 setTimeout(function () {
37781 highlight(token.text, token.lang, function (err, code) {
37786 if (code != null && code !== token.text) {
37788 token.escaped = true;
37793 if (pending === 0) {
37801 if (pending === 0) {
37809 var _tokens = Lexer_1.lex(src, opt);
37811 if (opt.walkTokens) {
37812 marked.walkTokens(_tokens, opt.walkTokens);
37815 return Parser_1.parse(_tokens, opt);
37817 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
37820 return '<p>An error occurred:</p><pre>' + escape$3(e.message + '', true) + '</pre>';
37831 marked.options = marked.setOptions = function (opt) {
37832 merge$3(marked.defaults, opt);
37833 changeDefaults(marked.defaults);
37837 marked.getDefaults = getDefaults;
37838 marked.defaults = defaults$5;
37843 marked.use = function (extension) {
37844 var opts = merge$3({}, extension);
37846 if (extension.renderer) {
37848 var renderer = marked.defaults.renderer || new Renderer_1();
37850 var _loop = function _loop(prop) {
37851 var prevRenderer = renderer[prop];
37853 renderer[prop] = function () {
37854 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
37855 args[_key] = arguments[_key];
37858 var ret = extension.renderer[prop].apply(renderer, args);
37860 if (ret === false) {
37861 ret = prevRenderer.apply(renderer, args);
37868 for (var prop in extension.renderer) {
37872 opts.renderer = renderer;
37876 if (extension.tokenizer) {
37878 var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
37880 var _loop2 = function _loop2(prop) {
37881 var prevTokenizer = tokenizer[prop];
37883 tokenizer[prop] = function () {
37884 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
37885 args[_key2] = arguments[_key2];
37888 var ret = extension.tokenizer[prop].apply(tokenizer, args);
37890 if (ret === false) {
37891 ret = prevTokenizer.apply(tokenizer, args);
37898 for (var prop in extension.tokenizer) {
37902 opts.tokenizer = tokenizer;
37906 if (extension.walkTokens) {
37907 var walkTokens = marked.defaults.walkTokens;
37909 opts.walkTokens = function (token) {
37910 extension.walkTokens(token);
37918 marked.setOptions(opts);
37921 * Run callback for every token
37925 marked.walkTokens = function (tokens, callback) {
37926 var _iterator = _createForOfIteratorHelper(tokens),
37930 for (_iterator.s(); !(_step = _iterator.n()).done;) {
37931 var token = _step.value;
37934 switch (token.type) {
37937 var _iterator2 = _createForOfIteratorHelper(token.tokens.header),
37941 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
37942 var cell = _step2.value;
37943 marked.walkTokens(cell, callback);
37951 var _iterator3 = _createForOfIteratorHelper(token.tokens.cells),
37955 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
37956 var row = _step3.value;
37958 var _iterator4 = _createForOfIteratorHelper(row),
37962 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
37963 var _cell = _step4.value;
37964 marked.walkTokens(_cell, callback);
37983 marked.walkTokens(token.items, callback);
37989 if (token.tokens) {
37990 marked.walkTokens(token.tokens, callback);
38006 marked.parseInline = function (src, opt) {
38007 // throw error in case of non string input
38008 if (typeof src === 'undefined' || src === null) {
38009 throw new Error('marked.parseInline(): input parameter is undefined or null');
38012 if (typeof src !== 'string') {
38013 throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
38016 opt = merge$3({}, marked.defaults, opt || {});
38017 checkSanitizeDeprecation$1(opt);
38020 var tokens = Lexer_1.lexInline(src, opt);
38022 if (opt.walkTokens) {
38023 marked.walkTokens(tokens, opt.walkTokens);
38026 return Parser_1.parseInline(tokens, opt);
38028 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
38031 return '<p>An error occurred:</p><pre>' + escape$3(e.message + '', true) + '</pre>';
38042 marked.Parser = Parser_1;
38043 marked.parser = Parser_1.parse;
38044 marked.Renderer = Renderer_1;
38045 marked.TextRenderer = TextRenderer_1;
38046 marked.Lexer = Lexer_1;
38047 marked.lexer = Lexer_1.lex;
38048 marked.Tokenizer = Tokenizer_1;
38049 marked.Slugger = Slugger_1;
38050 marked.parse = marked;
38051 var marked_1 = marked;
38053 var tiler$2 = utilTiler();
38054 var dispatch$3 = dispatch('loaded');
38055 var _tileZoom$2 = 14;
38056 var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
38057 var _osmoseData = {
38060 }; // This gets reassigned if reset
38064 function abortRequest$2(controller) {
38066 controller.abort();
38070 function abortUnwantedRequests$2(cache, tiles) {
38071 Object.keys(cache.inflightTile).forEach(function (k) {
38072 var wanted = tiles.find(function (tile) {
38073 return k === tile.id;
38077 abortRequest$2(cache.inflightTile[k]);
38078 delete cache.inflightTile[k];
38083 function encodeIssueRtree$2(d) {
38091 } // Replace or remove QAItem from rtree
38094 function updateRtree$2(item, replace) {
38095 _cache$2.rtree.remove(item, function (a, b) {
38096 return a.data.id === b.data.id;
38100 _cache$2.rtree.insert(item);
38102 } // Issues shouldn't obscure each other
38105 function preventCoincident$1(loc) {
38106 var coincident = false;
38109 // first time, move marker up. after that, move marker right.
38110 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
38111 loc = geoVecAdd(loc, delta);
38112 var bbox = geoExtent(loc).bbox();
38113 coincident = _cache$2.rtree.search(bbox).length;
38114 } while (coincident);
38119 var serviceOsmose = {
38121 init: function init() {
38122 _mainFileFetcher.get('qa_data').then(function (d) {
38123 _osmoseData = d.osmose;
38124 _osmoseData.items = Object.keys(d.osmose.icons).map(function (s) {
38125 return s.split('-')[0];
38126 }).reduce(function (unique, item) {
38127 return unique.indexOf(item) !== -1 ? unique : [].concat(_toConsumableArray(unique), [item]);
38135 this.event = utilRebind(this, dispatch$3, 'on');
38137 reset: function reset() {
38142 Object.values(_cache$2.inflightTile).forEach(abortRequest$2); // Strings and colors are static and should not be re-populated
38144 _strings = _cache$2.strings;
38145 _colors = _cache$2.colors;
38154 rtree: new RBush(),
38159 loadIssues: function loadIssues(projection) {
38163 // Tiles return a maximum # of issues
38164 // So we want to filter our request for only types iD supports
38165 item: _osmoseData.items
38166 }; // determine the needed tiles to cover the view
38168 var tiles = tiler$2.zoomExtent([_tileZoom$2, _tileZoom$2]).getTiles(projection); // abort inflight requests that are no longer needed
38170 abortUnwantedRequests$2(_cache$2, tiles); // issue new requests..
38172 tiles.forEach(function (tile) {
38173 if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
38175 var _tile$xyz = _slicedToArray(tile.xyz, 3),
38180 var url = "".concat(_osmoseUrlRoot, "/issues/").concat(z, "/").concat(x, "/").concat(y, ".json?") + utilQsString(params);
38181 var controller = new AbortController();
38182 _cache$2.inflightTile[tile.id] = controller;
38184 signal: controller.signal
38185 }).then(function (data) {
38186 delete _cache$2.inflightTile[tile.id];
38187 _cache$2.loadedTile[tile.id] = true;
38189 if (data.features) {
38190 data.features.forEach(function (issue) {
38191 var _issue$properties = issue.properties,
38192 item = _issue$properties.item,
38193 cl = _issue$properties["class"],
38194 id = _issue$properties.uuid;
38195 /* Osmose issues are uniquely identified by a unique
38196 `item` and `class` combination (both integer values) */
38198 var itemType = "".concat(item, "-").concat(cl); // Filter out unsupported issue types (some are too specific or advanced)
38200 if (itemType in _osmoseData.icons) {
38201 var loc = issue.geometry.coordinates; // lon, lat
38203 loc = preventCoincident$1(loc);
38204 var d = new QAItem(loc, _this, itemType, id, {
38206 }); // Setting elems here prevents UI detail requests
38208 if (item === 8300 || item === 8360) {
38212 _cache$2.data[d.id] = d;
38214 _cache$2.rtree.insert(encodeIssueRtree$2(d));
38219 dispatch$3.call('loaded');
38220 })["catch"](function () {
38221 delete _cache$2.inflightTile[tile.id];
38222 _cache$2.loadedTile[tile.id] = true;
38226 loadIssueDetail: function loadIssueDetail(issue) {
38229 // Issue details only need to be fetched once
38230 if (issue.elems !== undefined) {
38231 return Promise.resolve(issue);
38234 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "?langs=").concat(_mainLocalizer.localeCode());
38236 var cacheDetails = function cacheDetails(data) {
38237 // Associated elements used for highlighting
38238 // Assign directly for immediate use in the callback
38239 issue.elems = data.elems.map(function (e) {
38240 return e.type.substring(0, 1) + e.id;
38241 }); // Some issues have instance specific detail in a subtitle
38243 issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
38245 _this2.replaceItem(issue);
38248 return d3_json(url).then(cacheDetails).then(function () {
38252 loadStrings: function loadStrings() {
38253 var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _mainLocalizer.localeCode();
38254 var items = Object.keys(_osmoseData.icons);
38256 if (locale in _cache$2.strings && Object.keys(_cache$2.strings[locale]).length === items.length) {
38257 return Promise.resolve(_cache$2.strings[locale]);
38258 } // May be partially populated already if some requests were successful
38261 if (!(locale in _cache$2.strings)) {
38262 _cache$2.strings[locale] = {};
38263 } // Only need to cache strings for supported issue types
38264 // Using multiple individual item + class requests to reduce fetched data size
38267 var allRequests = items.map(function (itemType) {
38268 // No need to request data we already have
38269 if (itemType in _cache$2.strings[locale]) return null;
38271 var cacheData = function cacheData(data) {
38272 // Bunch of nested single value arrays of objects
38273 var _data$categories = _slicedToArray(data.categories, 1),
38274 _data$categories$ = _data$categories[0],
38275 cat = _data$categories$ === void 0 ? {
38277 } : _data$categories$;
38279 var _cat$items = _slicedToArray(cat.items, 1),
38280 _cat$items$ = _cat$items[0],
38281 item = _cat$items$ === void 0 ? {
38285 var _item$class = _slicedToArray(item["class"], 1),
38286 _item$class$ = _item$class[0],
38287 cl = _item$class$ === void 0 ? null : _item$class$; // If null default value is reached, data wasn't as expected (or was empty)
38291 /* eslint-disable no-console */
38292 console.log("Osmose strings request (".concat(itemType, ") had unexpected data"));
38293 /* eslint-enable no-console */
38296 } // Cache served item colors to automatically style issue markers later
38299 var itemInt = item.item,
38300 color = item.color;
38302 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
38303 _cache$2.colors[itemInt] = color;
38304 } // Value of root key will be null if no string exists
38305 // If string exists, value is an object with key 'auto' for string
38308 var title = cl.title,
38309 detail = cl.detail,
38311 trap = cl.trap; // Osmose titles shouldn't contain markdown
38313 var issueStrings = {};
38314 if (title) issueStrings.title = title.auto;
38315 if (detail) issueStrings.detail = marked_1(detail.auto);
38316 if (trap) issueStrings.trap = marked_1(trap.auto);
38317 if (fix) issueStrings.fix = marked_1(fix.auto);
38318 _cache$2.strings[locale][itemType] = issueStrings;
38321 var _itemType$split = itemType.split('-'),
38322 _itemType$split2 = _slicedToArray(_itemType$split, 2),
38323 item = _itemType$split2[0],
38324 cl = _itemType$split2[1]; // Osmose API falls back to English strings where untranslated or if locale doesn't exist
38327 var url = "".concat(_osmoseUrlRoot, "/items/").concat(item, "/class/").concat(cl, "?langs=").concat(locale);
38328 return d3_json(url).then(cacheData);
38329 }).filter(Boolean);
38330 return Promise.all(allRequests).then(function () {
38331 return _cache$2.strings[locale];
38334 getStrings: function getStrings(itemType) {
38335 var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _mainLocalizer.localeCode();
38336 // No need to fallback to English, Osmose API handles this for us
38337 return locale in _cache$2.strings ? _cache$2.strings[locale][itemType] : {};
38339 getColor: function getColor(itemType) {
38340 return itemType in _cache$2.colors ? _cache$2.colors[itemType] : '#FFFFFF';
38342 postUpdate: function postUpdate(issue, callback) {
38345 if (_cache$2.inflightPost[issue.id]) {
38347 message: 'Issue update already inflight',
38350 } // UI sets the status to either 'done' or 'false'
38353 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "/").concat(issue.newStatus);
38354 var controller = new AbortController();
38356 var after = function after() {
38357 delete _cache$2.inflightPost[issue.id];
38359 _this3.removeItem(issue);
38361 if (issue.newStatus === 'done') {
38362 // Keep track of the number of issues closed per `item` to tag the changeset
38363 if (!(issue.item in _cache$2.closed)) {
38364 _cache$2.closed[issue.item] = 0;
38367 _cache$2.closed[issue.item] += 1;
38370 if (callback) callback(null, issue);
38373 _cache$2.inflightPost[issue.id] = controller;
38375 signal: controller.signal
38376 }).then(after)["catch"](function (err) {
38377 delete _cache$2.inflightPost[issue.id];
38378 if (callback) callback(err.message);
38381 // Get all cached QAItems covering the viewport
38382 getItems: function getItems(projection) {
38383 var viewport = projection.clipExtent();
38384 var min = [viewport[0][0], viewport[1][1]];
38385 var max = [viewport[1][0], viewport[0][1]];
38386 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
38387 return _cache$2.rtree.search(bbox).map(function (d) {
38391 // Get a QAItem from cache
38392 // NOTE: Don't change method name until UI v3 is merged
38393 getError: function getError(id) {
38394 return _cache$2.data[id];
38396 // get the name of the icon to display for this item
38397 getIcon: function getIcon(itemType) {
38398 return _osmoseData.icons[itemType];
38400 // Replace a single QAItem in the cache
38401 replaceItem: function replaceItem(item) {
38402 if (!(item instanceof QAItem) || !item.id) return;
38403 _cache$2.data[item.id] = item;
38404 updateRtree$2(encodeIssueRtree$2(item), true); // true = replace
38408 // Remove a single QAItem from the cache
38409 removeItem: function removeItem(item) {
38410 if (!(item instanceof QAItem) || !item.id) return;
38411 delete _cache$2.data[item.id];
38412 updateRtree$2(encodeIssueRtree$2(item), false); // false = remove
38414 // Used to populate `closed:osmose:*` changeset tags
38415 getClosedCounts: function getClosedCounts() {
38416 return _cache$2.closed;
38418 itemURL: function itemURL(item) {
38419 return "https://osmose.openstreetmap.fr/en/error/".concat(item.id);
38423 var apibase = 'https://a.mapillary.com/v3/';
38424 var viewercss = 'mapillary-js/mapillary.min.css';
38425 var viewerjs = 'mapillary-js/mapillary.min.js';
38426 var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
38427 var mapFeatureConfig = {
38428 values: ['construction--flat--crosswalk-plain', 'marking--discrete--crosswalk-zebra', 'object--banner', 'object--bench', 'object--bike-rack', 'object--billboard', 'object--catch-basin', 'object--cctv-camera', 'object--fire-hydrant', 'object--mailbox', 'object--manhole', 'object--phone-booth', 'object--sign--advertisement', 'object--sign--information', 'object--sign--store', 'object--street-light', 'object--support--utility-pole', 'object--traffic-light--*', 'object--traffic-light--pedestrians', 'object--trash-can'].join(',')
38430 var maxResults = 1000;
38432 var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
38433 var dispatch$4 = dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'nodeChanged');
38434 var _mlyFallback = false;
38440 var _mlyActiveImage;
38442 var _mlySelectedImageKey;
38446 var _mlyViewerFilter = ['all'];
38448 var _loadViewerPromise;
38450 var _mlyHighlightedDetection;
38452 var _mlyShowFeatureDetections = false;
38453 var _mlyShowSignDetections = false;
38455 function abortRequest$3(controller) {
38456 controller.abort();
38459 function loadTiles(which, url, projection) {
38460 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
38461 var tiles = tiler$3.getTiles(projection); // abort inflight requests that are no longer needed
38463 var cache = _mlyCache[which];
38464 Object.keys(cache.inflight).forEach(function (k) {
38465 var wanted = tiles.find(function (tile) {
38466 return k.indexOf(tile.id + ',') === 0;
38470 abortRequest$3(cache.inflight[k]);
38471 delete cache.inflight[k];
38474 tiles.forEach(function (tile) {
38475 loadNextTilePage(which, currZoom, url, tile);
38479 function loadNextTilePage(which, currZoom, url, tile) {
38480 var cache = _mlyCache[which];
38481 var rect = tile.extent.rectangle();
38482 var maxPages = maxPageAtZoom(currZoom);
38483 var nextPage = cache.nextPage[tile.id] || 0;
38484 var nextURL = cache.nextURL[tile.id] || url + utilQsString({
38485 per_page: maxResults,
38487 client_id: clientId,
38488 bbox: [rect[0], rect[1], rect[2], rect[3]].join(',')
38490 if (nextPage > maxPages) return;
38491 var id = tile.id + ',' + String(nextPage);
38492 if (cache.loaded[id] || cache.inflight[id]) return;
38493 var controller = new AbortController();
38494 cache.inflight[id] = controller;
38497 signal: controller.signal,
38499 'Content-Type': 'application/json'
38502 fetch(nextURL, options).then(function (response) {
38503 if (!response.ok) {
38504 throw new Error(response.status + ' ' + response.statusText);
38507 var linkHeader = response.headers.get('Link');
38510 var pagination = parsePagination(linkHeader);
38512 if (pagination.next) {
38513 cache.nextURL[tile.id] = pagination.next;
38517 return response.json();
38518 }).then(function (data) {
38519 cache.loaded[id] = true;
38520 delete cache.inflight[id];
38522 if (!data || !data.features || !data.features.length) {
38523 throw new Error('No Data');
38526 var features = data.features.map(function (feature) {
38527 var loc = feature.geometry.coordinates;
38528 var d; // An image (shown as a green dot on the map) is a single street photo with extra
38529 // information such as location, camera angle (CA), camera model, and so on.
38530 // Each image feature is a GeoJSON Point
38532 if (which === 'images') {
38535 key: feature.properties.key,
38536 ca: feature.properties.ca,
38537 captured_at: feature.properties.captured_at,
38538 captured_by: feature.properties.username,
38539 pano: feature.properties.pano
38541 cache.forImageKey[d.key] = d; // cache imageKey -> image
38542 // Mapillary organizes images as sequences. A sequence of images are continuously captured
38543 // by a user at a give time. Sequences are shown on the map as green lines.
38544 // Each sequence feature is a GeoJSON LineString
38545 } else if (which === 'sequences') {
38546 var sequenceKey = feature.properties.key;
38547 cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString
38549 feature.properties.coordinateProperties.image_keys.forEach(function (imageKey) {
38550 cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey
38552 return false; // because no `d` data worth loading into an rbush
38553 // A map feature is a real world object that can be shown on a map. It could be any object
38554 // recognized from images, manually added in images, or added on the map.
38555 // Each map feature is a GeoJSON Point (located where the feature is)
38556 } else if (which === 'map_features' || which === 'points') {
38559 key: feature.properties.key,
38560 value: feature.properties.value,
38561 "package": feature.properties["package"],
38562 detections: feature.properties.detections
38573 }).filter(Boolean);
38575 if (cache.rtree && features) {
38576 cache.rtree.load(features);
38579 if (data.features.length === maxResults) {
38580 // more pages to load
38581 cache.nextPage[tile.id] = nextPage + 1;
38582 loadNextTilePage(which, currZoom, url, tile);
38584 cache.nextPage[tile.id] = Infinity; // no more pages to load
38587 if (which === 'images' || which === 'sequences') {
38588 dispatch$4.call('loadedImages');
38589 } else if (which === 'map_features') {
38590 dispatch$4.call('loadedSigns');
38591 } else if (which === 'points') {
38592 dispatch$4.call('loadedMapFeatures');
38594 })["catch"](function () {
38595 cache.loaded[id] = true;
38596 delete cache.inflight[id];
38600 function loadData(which, url) {
38601 var cache = _mlyCache[which];
38605 'Content-Type': 'application/json'
38608 var nextUrl = url + '&client_id=' + clientId;
38609 return fetch(nextUrl, options).then(function (response) {
38610 if (!response.ok) {
38611 throw new Error(response.status + ' ' + response.statusText);
38614 return response.json();
38615 }).then(function (data) {
38616 if (!data || !data.features || !data.features.length) {
38617 throw new Error('No Data');
38620 data.features.forEach(function (feature) {
38623 if (which === 'image_detections') {
38625 key: feature.properties.key,
38626 image_key: feature.properties.image_key,
38627 value: feature.properties.value,
38628 "package": feature.properties["package"],
38629 shape: feature.properties.shape
38632 if (!cache.forImageKey[d.image_key]) {
38633 cache.forImageKey[d.image_key] = [];
38636 cache.forImageKey[d.image_key].push(d);
38642 function maxPageAtZoom(z) {
38643 if (z < 15) return 2;
38644 if (z === 15) return 5;
38645 if (z === 16) return 10;
38646 if (z === 17) return 20;
38647 if (z === 18) return 40;
38648 if (z > 18) return 80;
38649 } // extract links to pages of API results
38652 function parsePagination(links) {
38653 return links.split(',').map(function (rel) {
38654 var elements = rel.split(';');
38656 if (elements.length === 2) {
38657 return [/<(.+)>/.exec(elements[0])[1], /rel="(.+)"/.exec(elements[1])[1]];
38661 }).reduce(function (pagination, val) {
38662 pagination[val[1]] = val[0];
38665 } // partition viewport into higher zoom tiles
38668 function partitionViewport(projection) {
38669 var z = geoScaleToZoom(projection.scale());
38670 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
38672 var tiler = utilTiler().zoomExtent([z2, z2]);
38673 return tiler.getTiles(projection).map(function (tile) {
38674 return tile.extent;
38676 } // no more than `limit` results per partition.
38679 function searchLimited(limit, projection, rtree) {
38680 limit = limit || 5;
38681 return partitionViewport(projection).reduce(function (result, extent) {
38682 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
38685 return found.length ? result.concat(found) : result;
38689 var serviceMapillary = {
38690 init: function init() {
38695 this.event = utilRebind(this, dispatch$4, 'on');
38697 reset: function reset() {
38699 Object.values(_mlyCache.images.inflight).forEach(abortRequest$3);
38700 Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3);
38701 Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3);
38702 Object.values(_mlyCache.points.inflight).forEach(abortRequest$3);
38703 Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3);
38712 rtree: new RBush(),
38715 image_detections: {
38741 rtree: new RBush(),
38746 _mlySelectedImageKey = null;
38747 _mlyActiveImage = null;
38750 images: function images(projection) {
38752 return searchLimited(limit, projection, _mlyCache.images.rtree);
38754 signs: function signs(projection) {
38756 return searchLimited(limit, projection, _mlyCache.map_features.rtree);
38758 mapFeatures: function mapFeatures(projection) {
38760 return searchLimited(limit, projection, _mlyCache.points.rtree);
38762 cachedImage: function cachedImage(imageKey) {
38763 return _mlyCache.images.forImageKey[imageKey];
38765 sequences: function sequences(projection) {
38766 var viewport = projection.clipExtent();
38767 var min = [viewport[0][0], viewport[1][1]];
38768 var max = [viewport[1][0], viewport[0][1]];
38769 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
38770 var sequenceKeys = {}; // all sequences for images in viewport
38772 _mlyCache.images.rtree.search(bbox).forEach(function (d) {
38773 var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
38776 sequenceKeys[sequenceKey] = true;
38778 }); // Return lineStrings for the sequences
38781 return Object.keys(sequenceKeys).map(function (sequenceKey) {
38782 return _mlyCache.sequences.lineString[sequenceKey];
38785 signsSupported: function signsSupported() {
38788 loadImages: function loadImages(projection) {
38789 loadTiles('images', apibase + 'images?sort_by=key&', projection);
38790 loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection);
38792 loadSigns: function loadSigns(projection) {
38793 loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection);
38795 loadMapFeatures: function loadMapFeatures(projection) {
38796 loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
38798 ensureViewerLoaded: function ensureViewerLoaded(context) {
38799 if (_loadViewerPromise) return _loadViewerPromise; // add mly-wrapper
38801 var wrap = context.container().select('.photoviewer').selectAll('.mly-wrapper').data([0]);
38802 wrap.enter().append('div').attr('id', 'ideditor-mly').attr('class', 'photo-wrapper mly-wrapper').classed('hide', true);
38804 _loadViewerPromise = new Promise(function (resolve, reject) {
38805 var loadedCount = 0;
38807 function loaded() {
38808 loadedCount += 1; // wait until both files are loaded
38810 if (loadedCount === 2) resolve();
38813 var head = select('head'); // load mapillary-viewercss
38815 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 () {
38817 }); // load mapillary-viewerjs
38819 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 () {
38822 })["catch"](function () {
38823 _loadViewerPromise = null;
38824 }).then(function () {
38825 that.initViewer(context);
38827 return _loadViewerPromise;
38829 loadSignResources: function loadSignResources(context) {
38830 context.ui().svgDefs.addSprites(['mapillary-sprite'], false
38831 /* don't override colors */
38835 loadObjectResources: function loadObjectResources(context) {
38836 context.ui().svgDefs.addSprites(['mapillary-object-sprite'], false
38837 /* don't override colors */
38841 resetTags: function resetTags() {
38842 if (_mlyViewer && !_mlyFallback) {
38843 _mlyViewer.getComponent('tag').removeAll(); // remove previous detections
38847 showFeatureDetections: function showFeatureDetections(value) {
38848 _mlyShowFeatureDetections = value;
38850 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
38854 showSignDetections: function showSignDetections(value) {
38855 _mlyShowSignDetections = value;
38857 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
38861 filterViewer: function filterViewer(context) {
38862 var showsPano = context.photos().showsPanoramic();
38863 var showsFlat = context.photos().showsFlat();
38864 var fromDate = context.photos().fromDate();
38865 var toDate = context.photos().toDate();
38866 var usernames = context.photos().usernames();
38867 var filter = ['all'];
38868 if (!showsPano) filter.push(['==', 'pano', false]);
38869 if (!showsFlat && showsPano) filter.push(['==', 'pano', true]);
38870 if (usernames && usernames.length) filter.push(['==', 'username', usernames[0]]);
38873 var fromTimestamp = new Date(fromDate).getTime();
38874 filter.push(['>=', 'capturedAt', fromTimestamp]);
38878 var toTimestamp = new Date(toDate).getTime();
38879 filter.push(['>=', 'capturedAt', toTimestamp]);
38883 _mlyViewer.setFilter(filter);
38886 _mlyViewerFilter = filter;
38889 showViewer: function showViewer(context) {
38890 var wrap = context.container().select('.photoviewer').classed('hide', false);
38891 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
38893 if (isHidden && _mlyViewer) {
38894 wrap.selectAll('.photo-wrapper:not(.mly-wrapper)').classed('hide', true);
38895 wrap.selectAll('.photo-wrapper.mly-wrapper').classed('hide', false);
38897 _mlyViewer.resize();
38902 hideViewer: function hideViewer(context) {
38903 _mlyActiveImage = null;
38904 _mlySelectedImageKey = null;
38906 if (!_mlyFallback && _mlyViewer) {
38907 _mlyViewer.getComponent('sequence').stop();
38910 var viewer = context.container().select('.photoviewer');
38911 if (!viewer.empty()) viewer.datum(null);
38912 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
38913 this.updateUrlImage(null);
38914 dispatch$4.call('nodeChanged');
38915 return this.setStyles(context, null, true);
38917 parsePagination: parsePagination,
38918 updateUrlImage: function updateUrlImage(imageKey) {
38919 if (!window.mocha) {
38920 var hash = utilStringQs(window.location.hash);
38923 hash.photo = 'mapillary/' + imageKey;
38928 window.location.replace('#' + utilQsString(hash, true));
38931 highlightDetection: function highlightDetection(detection) {
38933 _mlyHighlightedDetection = detection.detection_key;
38938 initViewer: function initViewer(context) {
38940 if (!window.Mapillary) return;
38942 baseImageSize: 320,
38948 }; // Disable components requiring WebGL support
38950 if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) {
38951 _mlyFallback = true;
38962 navigation: true // fallback
38967 _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts);
38969 _mlyViewer.on('nodechanged', nodeChanged);
38971 _mlyViewer.on('bearingchanged', bearingChanged);
38973 if (_mlyViewerFilter) {
38974 _mlyViewer.setFilter(_mlyViewerFilter);
38975 } // Register viewer resize handler
38978 context.ui().photoviewer.on('resize.mapillary', function () {
38979 if (_mlyViewer) _mlyViewer.resize();
38980 }); // nodeChanged: called after the viewer has changed images and is ready.
38982 // There is some logic here to batch up clicks into a _mlyClicks array
38983 // because the user might click on a lot of markers quickly and nodechanged
38984 // may be called out of order asynchronously.
38986 // Clicks are added to the array in `selectedImage` and removed here.
38989 function nodeChanged(node) {
38991 var clicks = _mlyClicks;
38992 var index = clicks.indexOf(node.key);
38993 var selectedKey = _mlySelectedImageKey;
38994 that.setActiveImage(node);
38997 // `nodechanged` initiated from clicking on a marker..
38998 clicks.splice(index, 1); // remove the click
38999 // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()`
39000 // one more time to update the detections and attribution..
39002 if (node.key === selectedKey) {
39003 that.selectImage(context, _mlySelectedImageKey, true);
39006 // `nodechanged` initiated from the Mapillary viewer controls..
39007 var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
39008 context.map().centerEase(loc);
39009 that.selectImage(context, node.key, true);
39012 dispatch$4.call('nodeChanged');
39015 function bearingChanged(e) {
39016 dispatch$4.call('bearingChanged', undefined, e);
39019 // Pass in the image key string as `imageKey`.
39020 // This allows images to be selected from places that dont have access
39021 // to the full image datum (like the street signs layer or the js viewer)
39022 selectImage: function selectImage(context, imageKey, fromViewer) {
39023 _mlySelectedImageKey = imageKey;
39024 this.updateUrlImage(imageKey);
39025 var d = _mlyCache.images.forImageKey[imageKey];
39026 var viewer = context.container().select('.photoviewer');
39027 if (!viewer.empty()) viewer.datum(d);
39028 imageKey = d && d.key || imageKey;
39030 if (!fromViewer && imageKey) {
39031 _mlyClicks.push(imageKey);
39034 this.setStyles(context, null, true);
39036 if (_mlyShowFeatureDetections) {
39037 this.updateDetections(imageKey, apibase + 'image_detections?layers=points&values=' + mapFeatureConfig.values + '&image_keys=' + imageKey);
39040 if (_mlyShowSignDetections) {
39041 this.updateDetections(imageKey, apibase + 'image_detections?layers=trafficsigns&image_keys=' + imageKey);
39044 if (_mlyViewer && imageKey) {
39045 _mlyViewer.moveToKey(imageKey)["catch"](function (e) {
39046 console.error('mly3', e);
39047 }); // eslint-disable-line no-console
39053 getActiveImage: function getActiveImage() {
39054 return _mlyActiveImage;
39056 getSelectedImageKey: function getSelectedImageKey() {
39057 return _mlySelectedImageKey;
39059 getSequenceKeyForImageKey: function getSequenceKeyForImageKey(imageKey) {
39060 return _mlyCache.sequences.forImageKey[imageKey];
39062 setActiveImage: function setActiveImage(node) {
39064 _mlyActiveImage = {
39065 ca: node.originalCA,
39067 loc: [node.originalLatLon.lon, node.originalLatLon.lat],
39071 _mlyActiveImage = null;
39074 // Updates the currently highlighted sequence and selected bubble.
39075 // Reset is only necessary when interacting with the viewport because
39076 // this implicitly changes the currently selected bubble/sequence
39077 setStyles: function setStyles(context, hovered, reset) {
39079 // reset all layers
39080 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false);
39081 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
39084 var hoveredImageKey = hovered && hovered.key;
39085 var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey);
39086 var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
39087 var hoveredImageKeys = hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys || [];
39088 var selectedImageKey = _mlySelectedImageKey;
39089 var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey);
39090 var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
39091 var selectedImageKeys = selectedLineString && selectedLineString.properties.coordinateProperties.image_keys || []; // highlight sibling viewfields on either the selected or the hovered sequences
39093 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
39094 context.container().selectAll('.layer-mapillary .viewfield-group').classed('highlighted', function (d) {
39095 return highlightedImageKeys.indexOf(d.key) !== -1;
39096 }).classed('hovered', function (d) {
39097 return d.key === hoveredImageKey;
39099 context.container().selectAll('.layer-mapillary .sequence').classed('highlighted', function (d) {
39100 return d.properties.key === hoveredSequenceKey;
39101 }).classed('currentView', function (d) {
39102 return d.properties.key === selectedSequenceKey;
39103 }); // update viewfields if needed
39105 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
39107 function viewfieldPath() {
39108 var d = this.parentNode.__data__;
39110 if (d.pano && d.key !== selectedImageKey) {
39111 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
39113 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
39119 updateDetections: function updateDetections(imageKey, url) {
39120 if (!_mlyViewer || _mlyFallback) return;
39121 if (!imageKey) return;
39123 if (!_mlyCache.image_detections.forImageKey[imageKey]) {
39124 loadData('image_detections', url).then(function () {
39125 showDetections(_mlyCache.image_detections.forImageKey[imageKey] || []);
39128 showDetections(_mlyCache.image_detections.forImageKey[imageKey]);
39131 function showDetections(detections) {
39132 detections.forEach(function (data) {
39133 var tag = makeTag(data);
39136 var tagComponent = _mlyViewer.getComponent('tag');
39138 tagComponent.add([tag]);
39143 function makeTag(data) {
39144 var valueParts = data.value.split('--');
39145 if (!valueParts.length) return;
39148 var color = 0xffffff;
39150 if (_mlyHighlightedDetection === data.key) {
39152 text = valueParts[1];
39154 if (text === 'flat' || text === 'discrete' || text === 'sign') {
39155 text = valueParts[2];
39158 text = text.replace(/-/g, ' ');
39159 text = text.charAt(0).toUpperCase() + text.slice(1);
39160 _mlyHighlightedDetection = null;
39163 if (data.shape.type === 'Polygon') {
39164 var polygonGeometry = new Mapillary.TagComponent.PolygonGeometry(data.shape.coordinates[0]);
39165 tag = new Mapillary.TagComponent.OutlineTag(data.key, polygonGeometry, {
39173 } else if (data.shape.type === 'Point') {
39174 var pointGeometry = new Mapillary.TagComponent.PointGeometry(data.shape.coordinates[0]);
39175 tag = new Mapillary.TagComponent.SpotTag(data.key, pointGeometry, {
39185 cache: function cache() {
39190 function validationIssue(attrs) {
39191 this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag')
39193 this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
39195 this.severity = attrs.severity; // required - 'warning' or 'error'
39197 this.message = attrs.message; // required - function returning localized string
39199 this.reference = attrs.reference; // optional - function(selection) to render reference information
39201 this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue
39203 this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue
39205 this.data = attrs.data; // optional - object containing extra data for the fixes
39207 this.dynamicFixes = attrs.dynamicFixes; // optional - function(context) returning fixes
39209 this.hash = attrs.hash; // optional - string to further differentiate the issue
39211 this.id = generateID.apply(this); // generated - see below
39213 this.autoFix = null; // generated - if autofix exists, will be set below
39214 // A unique, deterministic string hash.
39215 // Issues with identical id values are considered identical.
39217 function generateID() {
39218 var parts = [this.type];
39221 // subclasses can pass in their own differentiator
39222 parts.push(this.hash);
39225 if (this.subtype) {
39226 parts.push(this.subtype);
39227 } // include the entities this issue is for
39228 // (sort them so the id is deterministic)
39231 if (this.entityIds) {
39232 var entityKeys = this.entityIds.slice().sort();
39233 parts.push.apply(parts, entityKeys);
39236 return parts.join(':');
39239 this.extent = function (resolver) {
39241 return geoExtent(this.loc);
39244 if (this.entityIds && this.entityIds.length) {
39245 return this.entityIds.reduce(function (extent, entityId) {
39246 return extent.extend(resolver.entity(entityId).extent(resolver));
39253 this.fixes = function (context) {
39254 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
39257 if (issue.severity === 'warning') {
39258 // allow ignoring any issue that's not an error
39259 fixes.push(new validationIssueFix({
39260 title: _t.html('issues.fix.ignore_issue.title'),
39261 icon: 'iD-icon-close',
39262 onClick: function onClick() {
39263 context.validator().ignoreIssue(this.issue.id);
39268 fixes.forEach(function (fix) {
39269 // the id doesn't matter as long as it's unique to this issue/fix
39270 fix.id = fix.title; // add a reference to the issue for use in actions
39274 if (fix.autoArgs) {
39275 issue.autoFix = fix;
39281 function validationIssueFix(attrs) {
39282 this.title = attrs.title; // Required
39284 this.onClick = attrs.onClick; // Optional - the function to run to apply the fix
39286 this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
39288 this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
39290 this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting.
39292 this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
39294 this.issue = null; // Generated link - added by validationIssue
39297 var buildRuleChecks = function buildRuleChecks() {
39299 equals: function equals(_equals) {
39300 return function (tags) {
39301 return Object.keys(_equals).every(function (k) {
39302 return _equals[k] === tags[k];
39306 notEquals: function notEquals(_notEquals) {
39307 return function (tags) {
39308 return Object.keys(_notEquals).some(function (k) {
39309 return _notEquals[k] !== tags[k];
39313 absence: function absence(_absence) {
39314 return function (tags) {
39315 return Object.keys(tags).indexOf(_absence) === -1;
39318 presence: function presence(_presence) {
39319 return function (tags) {
39320 return Object.keys(tags).indexOf(_presence) > -1;
39323 greaterThan: function greaterThan(_greaterThan) {
39324 var key = Object.keys(_greaterThan)[0];
39325 var value = _greaterThan[key];
39326 return function (tags) {
39327 return tags[key] > value;
39330 greaterThanEqual: function greaterThanEqual(_greaterThanEqual) {
39331 var key = Object.keys(_greaterThanEqual)[0];
39332 var value = _greaterThanEqual[key];
39333 return function (tags) {
39334 return tags[key] >= value;
39337 lessThan: function lessThan(_lessThan) {
39338 var key = Object.keys(_lessThan)[0];
39339 var value = _lessThan[key];
39340 return function (tags) {
39341 return tags[key] < value;
39344 lessThanEqual: function lessThanEqual(_lessThanEqual) {
39345 var key = Object.keys(_lessThanEqual)[0];
39346 var value = _lessThanEqual[key];
39347 return function (tags) {
39348 return tags[key] <= value;
39351 positiveRegex: function positiveRegex(_positiveRegex) {
39352 var tagKey = Object.keys(_positiveRegex)[0];
39354 var expression = _positiveRegex[tagKey].join('|');
39356 var regex = new RegExp(expression);
39357 return function (tags) {
39358 return regex.test(tags[tagKey]);
39361 negativeRegex: function negativeRegex(_negativeRegex) {
39362 var tagKey = Object.keys(_negativeRegex)[0];
39364 var expression = _negativeRegex[tagKey].join('|');
39366 var regex = new RegExp(expression);
39367 return function (tags) {
39368 return !regex.test(tags[tagKey]);
39374 var buildLineKeys = function buildLineKeys() {
39390 var serviceMapRules = {
39391 init: function init() {
39392 this._ruleChecks = buildRuleChecks();
39393 this._validationRules = [];
39394 this._areaKeys = osmAreaKeys;
39395 this._lineKeys = buildLineKeys();
39397 // list of rules only relevant to tag checks...
39398 filterRuleChecks: function filterRuleChecks(selector) {
39399 var _ruleChecks = this._ruleChecks;
39400 return Object.keys(selector).reduce(function (rules, key) {
39401 if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
39402 rules.push(_ruleChecks[key](selector[key]));
39408 // builds tagMap from mapcss-parse selector object...
39409 buildTagMap: function buildTagMap(selector) {
39410 var getRegexValues = function getRegexValues(regexes) {
39411 return regexes.map(function (regex) {
39412 return regex.replace(/\$|\^/g, '');
39416 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
39418 var isRegex = /regex/gi.test(key);
39419 var isEqual = /equals/gi.test(key);
39421 if (isRegex || isEqual) {
39422 Object.keys(selector[key]).forEach(function (selectorKey) {
39423 values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
39425 if (expectedTags.hasOwnProperty(selectorKey)) {
39426 values = values.concat(expectedTags[selectorKey]);
39429 expectedTags[selectorKey] = values;
39431 } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
39432 var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
39433 values = [selector[key][tagKey]];
39435 if (expectedTags.hasOwnProperty(tagKey)) {
39436 values = values.concat(expectedTags[tagKey]);
39439 expectedTags[tagKey] = values;
39442 return expectedTags;
39446 // inspired by osmWay#isArea()
39447 inferGeometry: function inferGeometry(tagMap) {
39448 var _lineKeys = this._lineKeys;
39449 var _areaKeys = this._areaKeys;
39451 var keyValueDoesNotImplyArea = function keyValueDoesNotImplyArea(key) {
39452 return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
39455 var keyValueImpliesLine = function keyValueImpliesLine(key) {
39456 return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
39459 if (tagMap.hasOwnProperty('area')) {
39460 if (tagMap.area.indexOf('yes') > -1) {
39464 if (tagMap.area.indexOf('no') > -1) {
39469 for (var key in tagMap) {
39470 if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
39474 if (key in _lineKeys && keyValueImpliesLine(key)) {
39481 // adds from mapcss-parse selector check...
39482 addRule: function addRule(selector) {
39484 // checks relevant to mapcss-selector
39485 checks: this.filterRuleChecks(selector),
39486 // true if all conditions for a tag error are true..
39487 matches: function matches(entity) {
39488 return this.checks.every(function (check) {
39489 return check(entity.tags);
39492 // borrowed from Way#isArea()
39493 inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
39494 geometryMatches: function geometryMatches(entity, graph) {
39495 if (entity.type === 'node' || entity.type === 'relation') {
39496 return selector.geometry === entity.type;
39497 } else if (entity.type === 'way') {
39498 return this.inferredGeometry === entity.geometry(graph);
39501 // when geometries match and tag matches are present, return a warning...
39502 findIssues: function findIssues(entity, graph, issues) {
39503 if (this.geometryMatches(entity, graph) && this.matches(entity)) {
39504 var severity = Object.keys(selector).indexOf('error') > -1 ? 'error' : 'warning';
39505 var _message = selector[severity];
39506 issues.push(new validationIssue({
39508 severity: severity,
39509 message: function message() {
39512 entityIds: [entity.id]
39518 this._validationRules.push(rule);
39520 clearRules: function clearRules() {
39521 this._validationRules = [];
39523 // returns validationRules...
39524 validationRules: function validationRules() {
39525 return this._validationRules;
39527 // returns ruleChecks
39528 ruleChecks: function ruleChecks() {
39529 return this._ruleChecks;
39533 var apibase$1 = 'https://nominatim.openstreetmap.org/';
39534 var _inflight = {};
39536 var _nominatimCache;
39538 var serviceNominatim = {
39539 init: function init() {
39541 _nominatimCache = new RBush();
39543 reset: function reset() {
39544 Object.values(_inflight).forEach(function (controller) {
39545 controller.abort();
39548 _nominatimCache = new RBush();
39550 countryCode: function countryCode(location, callback) {
39551 this.reverse(location, function (err, result) {
39553 return callback(err);
39554 } else if (result.address) {
39555 return callback(null, result.address.country_code);
39557 return callback('Unable to geocode', null);
39561 reverse: function reverse(loc, callback) {
39562 var cached = _nominatimCache.search({
39569 if (cached.length > 0) {
39570 if (callback) callback(null, cached[0].data);
39581 var url = apibase$1 + 'reverse?' + utilQsString(params);
39582 if (_inflight[url]) return;
39583 var controller = new AbortController();
39584 _inflight[url] = controller;
39586 signal: controller.signal
39587 }).then(function (result) {
39588 delete _inflight[url];
39590 if (result && result.error) {
39591 throw new Error(result.error);
39594 var extent = geoExtent(loc).padByMeters(200);
39596 _nominatimCache.insert(Object.assign(extent.bbox(), {
39600 if (callback) callback(null, result);
39601 })["catch"](function (err) {
39602 delete _inflight[url];
39603 if (err.name === 'AbortError') return;
39604 if (callback) callback(err.message);
39607 search: function search(val, callback) {
39608 var searchVal = encodeURIComponent(val);
39609 var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
39610 if (_inflight[url]) return;
39611 var controller = new AbortController();
39612 _inflight[url] = controller;
39614 signal: controller.signal
39615 }).then(function (result) {
39616 delete _inflight[url];
39618 if (result && result.error) {
39619 throw new Error(result.error);
39622 if (callback) callback(null, result);
39623 })["catch"](function (err) {
39624 delete _inflight[url];
39625 if (err.name === 'AbortError') return;
39626 if (callback) callback(err.message);
39631 var apibase$2 = 'https://openstreetcam.org';
39632 var maxResults$1 = 1000;
39633 var tileZoom$1 = 14;
39634 var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
39635 var dispatch$5 = dispatch('loadedImages');
39636 var imgZoom = d3_zoom().extent([[0, 0], [320, 240]]).translateExtent([[0, 0], [320, 240]]).scaleExtent([1, 15]);
39640 var _oscSelectedImage;
39642 var _loadViewerPromise$1;
39644 function abortRequest$4(controller) {
39645 controller.abort();
39648 function maxPageAtZoom$1(z) {
39649 if (z < 15) return 2;
39650 if (z === 15) return 5;
39651 if (z === 16) return 10;
39652 if (z === 17) return 20;
39653 if (z === 18) return 40;
39654 if (z > 18) return 80;
39657 function loadTiles$1(which, url, projection) {
39658 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
39659 var tiles = tiler$4.getTiles(projection); // abort inflight requests that are no longer needed
39661 var cache = _oscCache[which];
39662 Object.keys(cache.inflight).forEach(function (k) {
39663 var wanted = tiles.find(function (tile) {
39664 return k.indexOf(tile.id + ',') === 0;
39668 abortRequest$4(cache.inflight[k]);
39669 delete cache.inflight[k];
39672 tiles.forEach(function (tile) {
39673 loadNextTilePage$1(which, currZoom, url, tile);
39677 function loadNextTilePage$1(which, currZoom, url, tile) {
39678 var cache = _oscCache[which];
39679 var bbox = tile.extent.bbox();
39680 var maxPages = maxPageAtZoom$1(currZoom);
39681 var nextPage = cache.nextPage[tile.id] || 1;
39682 var params = utilQsString({
39685 // client_id: clientId,
39686 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
39687 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
39689 if (nextPage > maxPages) return;
39690 var id = tile.id + ',' + String(nextPage);
39691 if (cache.loaded[id] || cache.inflight[id]) return;
39692 var controller = new AbortController();
39693 cache.inflight[id] = controller;
39696 signal: controller.signal,
39699 'Content-Type': 'application/x-www-form-urlencoded'
39702 d3_json(url, options).then(function (data) {
39703 cache.loaded[id] = true;
39704 delete cache.inflight[id];
39706 if (!data || !data.currentPageItems || !data.currentPageItems.length) {
39707 throw new Error('No Data');
39710 var features = data.currentPageItems.map(function (item) {
39711 var loc = [+item.lng, +item.lat];
39714 if (which === 'images') {
39719 captured_at: item.shot_date || item.date_added,
39720 captured_by: item.username,
39721 imagePath: item.lth_name,
39722 sequence_id: item.sequence_id,
39723 sequence_index: +item.sequence_index
39724 }; // cache sequence info
39726 var seq = _oscCache.sequences[d.sequence_id];
39733 _oscCache.sequences[d.sequence_id] = seq;
39736 seq.images[d.sequence_index] = d;
39737 _oscCache.images.forImageKey[d.key] = d; // cache imageKey -> image
39748 cache.rtree.load(features);
39750 if (data.currentPageItems.length === maxResults$1) {
39751 // more pages to load
39752 cache.nextPage[tile.id] = nextPage + 1;
39753 loadNextTilePage$1(which, currZoom, url, tile);
39755 cache.nextPage[tile.id] = Infinity; // no more pages to load
39758 if (which === 'images') {
39759 dispatch$5.call('loadedImages');
39761 })["catch"](function () {
39762 cache.loaded[id] = true;
39763 delete cache.inflight[id];
39765 } // partition viewport into higher zoom tiles
39768 function partitionViewport$1(projection) {
39769 var z = geoScaleToZoom(projection.scale());
39770 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
39772 var tiler = utilTiler().zoomExtent([z2, z2]);
39773 return tiler.getTiles(projection).map(function (tile) {
39774 return tile.extent;
39776 } // no more than `limit` results per partition.
39779 function searchLimited$1(limit, projection, rtree) {
39780 limit = limit || 5;
39781 return partitionViewport$1(projection).reduce(function (result, extent) {
39782 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
39785 return found.length ? result.concat(found) : result;
39789 var serviceOpenstreetcam = {
39790 init: function init() {
39795 this.event = utilRebind(this, dispatch$5, 'on');
39797 reset: function reset() {
39799 Object.values(_oscCache.images.inflight).forEach(abortRequest$4);
39807 rtree: new RBush(),
39812 _oscSelectedImage = null;
39814 images: function images(projection) {
39816 return searchLimited$1(limit, projection, _oscCache.images.rtree);
39818 sequences: function sequences(projection) {
39819 var viewport = projection.clipExtent();
39820 var min = [viewport[0][0], viewport[1][1]];
39821 var max = [viewport[1][0], viewport[0][1]];
39822 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
39823 var sequenceKeys = {}; // all sequences for images in viewport
39825 _oscCache.images.rtree.search(bbox).forEach(function (d) {
39826 sequenceKeys[d.data.sequence_id] = true;
39827 }); // make linestrings from those sequences
39830 var lineStrings = [];
39831 Object.keys(sequenceKeys).forEach(function (sequenceKey) {
39832 var seq = _oscCache.sequences[sequenceKey];
39833 var images = seq && seq.images;
39837 type: 'LineString',
39838 coordinates: images.map(function (d) {
39840 }).filter(Boolean),
39842 captured_at: images[0] ? images[0].captured_at : null,
39843 captured_by: images[0] ? images[0].captured_by : null,
39849 return lineStrings;
39851 cachedImage: function cachedImage(imageKey) {
39852 return _oscCache.images.forImageKey[imageKey];
39854 loadImages: function loadImages(projection) {
39855 var url = apibase$2 + '/1.0/list/nearby-photos/';
39856 loadTiles$1('images', url, projection);
39858 ensureViewerLoaded: function ensureViewerLoaded(context) {
39859 if (_loadViewerPromise$1) return _loadViewerPromise$1; // add osc-wrapper
39861 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper').data([0]);
39863 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper osc-wrapper').classed('hide', true).call(imgZoom.on('zoom', zoomPan)).on('dblclick.zoom', null);
39864 wrapEnter.append('div').attr('class', 'photo-attribution fillD');
39865 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
39866 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
39867 controlsEnter.append('button').on('click.rotate-ccw', rotate(-90)).html('⤿');
39868 controlsEnter.append('button').on('click.rotate-cw', rotate(90)).html('⤾');
39869 controlsEnter.append('button').on('click.forward', step(1)).html('►');
39870 wrapEnter.append('div').attr('class', 'osc-image-wrap'); // Register viewer resize handler
39872 context.ui().photoviewer.on('resize.openstreetcam', function (dimensions) {
39873 imgZoom = d3_zoom().extent([[0, 0], dimensions]).translateExtent([[0, 0], dimensions]).scaleExtent([1, 15]).on('zoom', zoomPan);
39876 function zoomPan(d3_event) {
39877 var t = d3_event.transform;
39878 context.container().select('.photoviewer .osc-image-wrap').call(utilSetTransform, t.x, t.y, t.k);
39881 function rotate(deg) {
39882 return function () {
39883 if (!_oscSelectedImage) return;
39884 var sequenceKey = _oscSelectedImage.sequence_id;
39885 var sequence = _oscCache.sequences[sequenceKey];
39886 if (!sequence) return;
39887 var r = sequence.rotation || 0;
39889 if (r > 180) r -= 360;
39890 if (r < -180) r += 360;
39891 sequence.rotation = r;
39892 var wrap = context.container().select('.photoviewer .osc-wrapper');
39893 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
39894 wrap.selectAll('.osc-image').transition().duration(100).style('transform', 'rotate(' + r + 'deg)');
39898 function step(stepBy) {
39899 return function () {
39900 if (!_oscSelectedImage) return;
39901 var sequenceKey = _oscSelectedImage.sequence_id;
39902 var sequence = _oscCache.sequences[sequenceKey];
39903 if (!sequence) return;
39904 var nextIndex = _oscSelectedImage.sequence_index + stepBy;
39905 var nextImage = sequence.images[nextIndex];
39906 if (!nextImage) return;
39907 context.map().centerEase(nextImage.loc);
39908 that.selectImage(context, nextImage.key);
39910 } // don't need any async loading so resolve immediately
39913 _loadViewerPromise$1 = Promise.resolve();
39914 return _loadViewerPromise$1;
39916 showViewer: function showViewer(context) {
39917 var viewer = context.container().select('.photoviewer').classed('hide', false);
39918 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
39921 viewer.selectAll('.photo-wrapper:not(.osc-wrapper)').classed('hide', true);
39922 viewer.selectAll('.photo-wrapper.osc-wrapper').classed('hide', false);
39927 hideViewer: function hideViewer(context) {
39928 _oscSelectedImage = null;
39929 this.updateUrlImage(null);
39930 var viewer = context.container().select('.photoviewer');
39931 if (!viewer.empty()) viewer.datum(null);
39932 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
39933 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
39934 return this.setStyles(context, null, true);
39936 selectImage: function selectImage(context, imageKey) {
39937 var d = this.cachedImage(imageKey);
39938 _oscSelectedImage = d;
39939 this.updateUrlImage(imageKey);
39940 var viewer = context.container().select('.photoviewer');
39941 if (!viewer.empty()) viewer.datum(d);
39942 this.setStyles(context, null, true);
39943 context.container().selectAll('.icon-sign').classed('currentView', false);
39944 if (!d) return this;
39945 var wrap = context.container().select('.photoviewer .osc-wrapper');
39946 var imageWrap = wrap.selectAll('.osc-image-wrap');
39947 var attribution = wrap.selectAll('.photo-attribution').html('');
39948 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
39949 imageWrap.selectAll('.osc-image').remove();
39952 var sequence = _oscCache.sequences[d.sequence_id];
39953 var r = sequence && sequence.rotation || 0;
39954 imageWrap.append('img').attr('class', 'osc-image').attr('src', apibase$2 + '/' + d.imagePath).style('transform', 'rotate(' + r + 'deg)');
39956 if (d.captured_by) {
39957 attribution.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by)).html('@' + d.captured_by);
39958 attribution.append('span').html('|');
39961 if (d.captured_at) {
39962 attribution.append('span').attr('class', 'captured_at').html(localeDateString(d.captured_at));
39963 attribution.append('span').html('|');
39966 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');
39971 function localeDateString(s) {
39972 if (!s) return null;
39978 var d = new Date(s);
39979 if (isNaN(d.getTime())) return null;
39980 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
39983 getSelectedImage: function getSelectedImage() {
39984 return _oscSelectedImage;
39986 getSequenceKeyForImage: function getSequenceKeyForImage(d) {
39987 return d && d.sequence_id;
39989 // Updates the currently highlighted sequence and selected bubble.
39990 // Reset is only necessary when interacting with the viewport because
39991 // this implicitly changes the currently selected bubble/sequence
39992 setStyles: function setStyles(context, hovered, reset) {
39994 // reset all layers
39995 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
39996 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
39999 var hoveredImageKey = hovered && hovered.key;
40000 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
40001 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
40002 var hoveredImageKeys = hoveredSequence && hoveredSequence.images.map(function (d) {
40005 var viewer = context.container().select('.photoviewer');
40006 var selected = viewer.empty() ? undefined : viewer.datum();
40007 var selectedImageKey = selected && selected.key;
40008 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
40009 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
40010 var selectedImageKeys = selectedSequence && selectedSequence.images.map(function (d) {
40012 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
40014 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
40015 context.container().selectAll('.layer-openstreetcam .viewfield-group').classed('highlighted', function (d) {
40016 return highlightedImageKeys.indexOf(d.key) !== -1;
40017 }).classed('hovered', function (d) {
40018 return d.key === hoveredImageKey;
40019 }).classed('currentView', function (d) {
40020 return d.key === selectedImageKey;
40022 context.container().selectAll('.layer-openstreetcam .sequence').classed('highlighted', function (d) {
40023 return d.properties.key === hoveredSequenceKey;
40024 }).classed('currentView', function (d) {
40025 return d.properties.key === selectedSequenceKey;
40026 }); // update viewfields if needed
40028 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
40030 function viewfieldPath() {
40031 var d = this.parentNode.__data__;
40033 if (d.pano && d.key !== selectedImageKey) {
40034 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
40036 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
40042 updateUrlImage: function updateUrlImage(imageKey) {
40043 if (!window.mocha) {
40044 var hash = utilStringQs(window.location.hash);
40047 hash.photo = 'openstreetcam/' + imageKey;
40052 window.location.replace('#' + utilQsString(hash, true));
40055 cache: function cache() {
40060 var FORCED$f = fails(function () {
40061 return new Date(NaN).toJSON() !== null
40062 || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1;
40065 // `Date.prototype.toJSON` method
40066 // https://tc39.github.io/ecma262/#sec-date.prototype.tojson
40067 _export({ target: 'Date', proto: true, forced: FORCED$f }, {
40068 // eslint-disable-next-line no-unused-vars
40069 toJSON: function toJSON(key) {
40070 var O = toObject(this);
40071 var pv = toPrimitive(O);
40072 return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString();
40076 // `URL.prototype.toJSON` method
40077 // https://url.spec.whatwg.org/#dom-url-tojson
40078 _export({ target: 'URL', proto: true, enumerable: true }, {
40079 toJSON: function toJSON() {
40080 return URL.prototype.toString.call(this);
40085 * Checks if `value` is the
40086 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
40087 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
40093 * @param {*} value The value to check.
40094 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
40100 * _.isObject([1, 2, 3]);
40103 * _.isObject(_.noop);
40106 * _.isObject(null);
40109 function isObject$1(value) {
40110 var type = _typeof(value);
40112 return value != null && (type == 'object' || type == 'function');
40115 /** Detect free variable `global` from Node.js. */
40116 var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global;
40118 /** Detect free variable `self`. */
40120 var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self;
40121 /** Used as a reference to the global object. */
40123 var root$1 = freeGlobal || freeSelf || Function('return this')();
40126 * Gets the timestamp of the number of milliseconds that have elapsed since
40127 * the Unix epoch (1 January 1970 00:00:00 UTC).
40133 * @returns {number} Returns the timestamp.
40136 * _.defer(function(stamp) {
40137 * console.log(_.now() - stamp);
40139 * // => Logs the number of milliseconds it took for the deferred invocation.
40142 var now$1 = function now() {
40143 return root$1.Date.now();
40146 /** Built-in value references. */
40148 var _Symbol = root$1.Symbol;
40150 /** Used for built-in method references. */
40152 var objectProto = Object.prototype;
40153 /** Used to check objects for own properties. */
40155 var hasOwnProperty$1 = objectProto.hasOwnProperty;
40157 * Used to resolve the
40158 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
40162 var nativeObjectToString = objectProto.toString;
40163 /** Built-in value references. */
40165 var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
40167 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
40170 * @param {*} value The value to query.
40171 * @returns {string} Returns the raw `toStringTag`.
40174 function getRawTag(value) {
40175 var isOwn = hasOwnProperty$1.call(value, symToStringTag),
40176 tag = value[symToStringTag];
40179 value[symToStringTag] = undefined;
40180 var unmasked = true;
40183 var result = nativeObjectToString.call(value);
40187 value[symToStringTag] = tag;
40189 delete value[symToStringTag];
40196 /** Used for built-in method references. */
40197 var objectProto$1 = Object.prototype;
40199 * Used to resolve the
40200 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
40204 var nativeObjectToString$1 = objectProto$1.toString;
40206 * Converts `value` to a string using `Object.prototype.toString`.
40209 * @param {*} value The value to convert.
40210 * @returns {string} Returns the converted string.
40213 function objectToString$1(value) {
40214 return nativeObjectToString$1.call(value);
40217 /** `Object#toString` result references. */
40219 var nullTag = '[object Null]',
40220 undefinedTag = '[object Undefined]';
40221 /** Built-in value references. */
40223 var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
40225 * The base implementation of `getTag` without fallbacks for buggy environments.
40228 * @param {*} value The value to query.
40229 * @returns {string} Returns the `toStringTag`.
40232 function baseGetTag(value) {
40233 if (value == null) {
40234 return value === undefined ? undefinedTag : nullTag;
40237 return symToStringTag$1 && symToStringTag$1 in Object(value) ? getRawTag(value) : objectToString$1(value);
40241 * Checks if `value` is object-like. A value is object-like if it's not `null`
40242 * and has a `typeof` result of "object".
40248 * @param {*} value The value to check.
40249 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
40252 * _.isObjectLike({});
40255 * _.isObjectLike([1, 2, 3]);
40258 * _.isObjectLike(_.noop);
40261 * _.isObjectLike(null);
40264 function isObjectLike(value) {
40265 return value != null && _typeof(value) == 'object';
40268 /** `Object#toString` result references. */
40270 var symbolTag = '[object Symbol]';
40272 * Checks if `value` is classified as a `Symbol` primitive or object.
40278 * @param {*} value The value to check.
40279 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
40282 * _.isSymbol(Symbol.iterator);
40285 * _.isSymbol('abc');
40289 function isSymbol$1(value) {
40290 return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
40293 /** Used as references for various `Number` constants. */
40296 /** Used to match leading and trailing whitespace. */
40298 var reTrim = /^\s+|\s+$/g;
40299 /** Used to detect bad signed hexadecimal string values. */
40301 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
40302 /** Used to detect binary string values. */
40304 var reIsBinary = /^0b[01]+$/i;
40305 /** Used to detect octal string values. */
40307 var reIsOctal = /^0o[0-7]+$/i;
40308 /** Built-in method references without a dependency on `root`. */
40310 var freeParseInt = parseInt;
40312 * Converts `value` to a number.
40318 * @param {*} value The value to process.
40319 * @returns {number} Returns the number.
40325 * _.toNumber(Number.MIN_VALUE);
40328 * _.toNumber(Infinity);
40331 * _.toNumber('3.2');
40335 function toNumber$1(value) {
40336 if (typeof value == 'number') {
40340 if (isSymbol$1(value)) {
40344 if (isObject$1(value)) {
40345 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
40346 value = isObject$1(other) ? other + '' : other;
40349 if (typeof value != 'string') {
40350 return value === 0 ? value : +value;
40353 value = value.replace(reTrim, '');
40354 var isBinary = reIsBinary.test(value);
40355 return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
40358 /** Error message constants. */
40360 var FUNC_ERROR_TEXT = 'Expected a function';
40361 /* Built-in method references for those with the same name as other `lodash` methods. */
40363 var nativeMax = Math.max,
40364 nativeMin = Math.min;
40366 * Creates a debounced function that delays invoking `func` until after `wait`
40367 * milliseconds have elapsed since the last time the debounced function was
40368 * invoked. The debounced function comes with a `cancel` method to cancel
40369 * delayed `func` invocations and a `flush` method to immediately invoke them.
40370 * Provide `options` to indicate whether `func` should be invoked on the
40371 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
40372 * with the last arguments provided to the debounced function. Subsequent
40373 * calls to the debounced function return the result of the last `func`
40376 * **Note:** If `leading` and `trailing` options are `true`, `func` is
40377 * invoked on the trailing edge of the timeout only if the debounced function
40378 * is invoked more than once during the `wait` timeout.
40380 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
40381 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
40383 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
40384 * for details over the differences between `_.debounce` and `_.throttle`.
40389 * @category Function
40390 * @param {Function} func The function to debounce.
40391 * @param {number} [wait=0] The number of milliseconds to delay.
40392 * @param {Object} [options={}] The options object.
40393 * @param {boolean} [options.leading=false]
40394 * Specify invoking on the leading edge of the timeout.
40395 * @param {number} [options.maxWait]
40396 * The maximum time `func` is allowed to be delayed before it's invoked.
40397 * @param {boolean} [options.trailing=true]
40398 * Specify invoking on the trailing edge of the timeout.
40399 * @returns {Function} Returns the new debounced function.
40402 * // Avoid costly calculations while the window size is in flux.
40403 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
40405 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
40406 * jQuery(element).on('click', _.debounce(sendMail, 300, {
40408 * 'trailing': false
40411 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
40412 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
40413 * var source = new EventSource('/stream');
40414 * jQuery(source).on('message', debounced);
40416 * // Cancel the trailing debounced invocation.
40417 * jQuery(window).on('popstate', debounced.cancel);
40420 function debounce(func, wait, options) {
40427 lastInvokeTime = 0,
40432 if (typeof func != 'function') {
40433 throw new TypeError(FUNC_ERROR_TEXT);
40436 wait = toNumber$1(wait) || 0;
40438 if (isObject$1(options)) {
40439 leading = !!options.leading;
40440 maxing = 'maxWait' in options;
40441 maxWait = maxing ? nativeMax(toNumber$1(options.maxWait) || 0, wait) : maxWait;
40442 trailing = 'trailing' in options ? !!options.trailing : trailing;
40445 function invokeFunc(time) {
40446 var args = lastArgs,
40447 thisArg = lastThis;
40448 lastArgs = lastThis = undefined;
40449 lastInvokeTime = time;
40450 result = func.apply(thisArg, args);
40454 function leadingEdge(time) {
40455 // Reset any `maxWait` timer.
40456 lastInvokeTime = time; // Start the timer for the trailing edge.
40458 timerId = setTimeout(timerExpired, wait); // Invoke the leading edge.
40460 return leading ? invokeFunc(time) : result;
40463 function remainingWait(time) {
40464 var timeSinceLastCall = time - lastCallTime,
40465 timeSinceLastInvoke = time - lastInvokeTime,
40466 timeWaiting = wait - timeSinceLastCall;
40467 return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
40470 function shouldInvoke(time) {
40471 var timeSinceLastCall = time - lastCallTime,
40472 timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the
40473 // trailing edge, the system time has gone backwards and we're treating
40474 // it as the trailing edge, or we've hit the `maxWait` limit.
40476 return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
40479 function timerExpired() {
40480 var time = now$1();
40482 if (shouldInvoke(time)) {
40483 return trailingEdge(time);
40484 } // Restart the timer.
40487 timerId = setTimeout(timerExpired, remainingWait(time));
40490 function trailingEdge(time) {
40491 timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been
40492 // debounced at least once.
40494 if (trailing && lastArgs) {
40495 return invokeFunc(time);
40498 lastArgs = lastThis = undefined;
40502 function cancel() {
40503 if (timerId !== undefined) {
40504 clearTimeout(timerId);
40507 lastInvokeTime = 0;
40508 lastArgs = lastCallTime = lastThis = timerId = undefined;
40512 return timerId === undefined ? result : trailingEdge(now$1());
40515 function debounced() {
40516 var time = now$1(),
40517 isInvoking = shouldInvoke(time);
40518 lastArgs = arguments;
40520 lastCallTime = time;
40523 if (timerId === undefined) {
40524 return leadingEdge(lastCallTime);
40528 // Handle invocations in a tight loop.
40529 clearTimeout(timerId);
40530 timerId = setTimeout(timerExpired, wait);
40531 return invokeFunc(lastCallTime);
40535 if (timerId === undefined) {
40536 timerId = setTimeout(timerExpired, wait);
40542 debounced.cancel = cancel;
40543 debounced.flush = flush;
40547 /** Error message constants. */
40549 var FUNC_ERROR_TEXT$1 = 'Expected a function';
40551 * Creates a throttled function that only invokes `func` at most once per
40552 * every `wait` milliseconds. The throttled function comes with a `cancel`
40553 * method to cancel delayed `func` invocations and a `flush` method to
40554 * immediately invoke them. Provide `options` to indicate whether `func`
40555 * should be invoked on the leading and/or trailing edge of the `wait`
40556 * timeout. The `func` is invoked with the last arguments provided to the
40557 * throttled function. Subsequent calls to the throttled function return the
40558 * result of the last `func` invocation.
40560 * **Note:** If `leading` and `trailing` options are `true`, `func` is
40561 * invoked on the trailing edge of the timeout only if the throttled function
40562 * is invoked more than once during the `wait` timeout.
40564 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
40565 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
40567 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
40568 * for details over the differences between `_.throttle` and `_.debounce`.
40573 * @category Function
40574 * @param {Function} func The function to throttle.
40575 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
40576 * @param {Object} [options={}] The options object.
40577 * @param {boolean} [options.leading=true]
40578 * Specify invoking on the leading edge of the timeout.
40579 * @param {boolean} [options.trailing=true]
40580 * Specify invoking on the trailing edge of the timeout.
40581 * @returns {Function} Returns the new throttled function.
40584 * // Avoid excessively updating the position while scrolling.
40585 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
40587 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
40588 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
40589 * jQuery(element).on('click', throttled);
40591 * // Cancel the trailing throttled invocation.
40592 * jQuery(window).on('popstate', throttled.cancel);
40595 function throttle(func, wait, options) {
40596 var leading = true,
40599 if (typeof func != 'function') {
40600 throw new TypeError(FUNC_ERROR_TEXT$1);
40603 if (isObject$1(options)) {
40604 leading = 'leading' in options ? !!options.leading : leading;
40605 trailing = 'trailing' in options ? !!options.trailing : trailing;
40608 return debounce(func, wait, {
40609 'leading': leading,
40611 'trailing': trailing
40615 var hashes = createCommonjsModule(function (module, exports) {
40617 * jshashes - https://github.com/h2non/jshashes
40618 * Released under the "New BSD" license
40620 * Algorithms specification:
40622 * MD5 - http://www.ietf.org/rfc/rfc1321.txt
40623 * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
40624 * SHA1 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40625 * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40626 * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40627 * HMAC - http://www.ietf.org/rfc/rfc2104.txt
40632 function utf8Encode(str) {
40639 if (str && str.length) {
40642 while ((i += 1) < l) {
40643 /* Decode utf-16 surrogate pairs */
40644 x = str.charCodeAt(i);
40645 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
40647 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
40648 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
40651 /* Encode output as utf-8 */
40655 output += String.fromCharCode(x);
40656 } else if (x <= 0x7FF) {
40657 output += String.fromCharCode(0xC0 | x >>> 6 & 0x1F, 0x80 | x & 0x3F);
40658 } else if (x <= 0xFFFF) {
40659 output += String.fromCharCode(0xE0 | x >>> 12 & 0x0F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
40660 } else if (x <= 0x1FFFFF) {
40661 output += String.fromCharCode(0xF0 | x >>> 18 & 0x07, 0x80 | x >>> 12 & 0x3F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
40669 function utf8Decode(str) {
40677 i = ac = c1 = c2 = c3 = 0;
40679 if (str && str.length) {
40684 c1 = str.charCodeAt(i);
40688 arr[ac] = String.fromCharCode(c1);
40690 } else if (c1 > 191 && c1 < 224) {
40691 c2 = str.charCodeAt(i + 1);
40692 arr[ac] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
40695 c2 = str.charCodeAt(i + 1);
40696 c3 = str.charCodeAt(i + 2);
40697 arr[ac] = String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
40703 return arr.join('');
40706 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
40707 * to work around bugs in some JS interpreters.
40711 function safe_add(x, y) {
40712 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
40713 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
40714 return msw << 16 | lsw & 0xFFFF;
40717 * Bitwise rotate a 32-bit number to the left.
40721 function bit_rol(num, cnt) {
40722 return num << cnt | num >>> 32 - cnt;
40725 * Convert a raw string to a hex string
40729 function rstr2hex(input, hexcase) {
40730 var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
40736 for (; i < l; i += 1) {
40737 x = input.charCodeAt(i);
40738 output += hex_tab.charAt(x >>> 4 & 0x0F) + hex_tab.charAt(x & 0x0F);
40744 * Convert an array of big-endian words to a string
40748 function binb2rstr(input) {
40750 l = input.length * 32,
40753 for (i = 0; i < l; i += 8) {
40754 output += String.fromCharCode(input[i >> 5] >>> 24 - i % 32 & 0xFF);
40760 * Convert an array of little-endian words to a string
40764 function binl2rstr(input) {
40766 l = input.length * 32,
40769 for (i = 0; i < l; i += 8) {
40770 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
40776 * Convert a raw string to an array of little-endian words
40777 * Characters >255 have their high-byte silently ignored.
40781 function rstr2binl(input) {
40783 l = input.length * 8,
40784 output = Array(input.length >> 2),
40785 lo = output.length;
40787 for (i = 0; i < lo; i += 1) {
40791 for (i = 0; i < l; i += 8) {
40792 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << i % 32;
40798 * Convert a raw string to an array of big-endian words
40799 * Characters >255 have their high-byte silently ignored.
40803 function rstr2binb(input) {
40805 l = input.length * 8,
40806 output = Array(input.length >> 2),
40807 lo = output.length;
40809 for (i = 0; i < lo; i += 1) {
40813 for (i = 0; i < l; i += 8) {
40814 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << 24 - i % 32;
40820 * Convert a raw string to an arbitrary string encoding
40824 function rstr2any(input, encoding) {
40825 var divisor = encoding.length,
40826 remainders = Array(),
40835 /* Convert to an array of 16-bit big-endian values, forming the dividend */
40837 dividend = Array(Math.ceil(input.length / 2));
40838 ld = dividend.length;
40840 for (i = 0; i < ld; i += 1) {
40841 dividend[i] = input.charCodeAt(i * 2) << 8 | input.charCodeAt(i * 2 + 1);
40844 * Repeatedly perform a long division. The binary array forms the dividend,
40845 * the length of the encoding is the divisor. Once computed, the quotient
40846 * forms the dividend for the next step. We stop when the dividend is zerHashes.
40847 * All remainders are stored for later use.
40851 while (dividend.length > 0) {
40852 quotient = Array();
40855 for (i = 0; i < dividend.length; i += 1) {
40856 x = (x << 16) + dividend[i];
40857 q = Math.floor(x / divisor);
40860 if (quotient.length > 0 || q > 0) {
40861 quotient[quotient.length] = q;
40865 remainders[remainders.length] = x;
40866 dividend = quotient;
40868 /* Convert the remainders to the output string */
40873 for (i = remainders.length - 1; i >= 0; i--) {
40874 output += encoding.charAt(remainders[i]);
40876 /* Append leading zero equivalents */
40879 full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
40881 for (i = output.length; i < full_length; i += 1) {
40882 output = encoding[0] + output;
40888 * Convert a raw string to a base-64 string
40892 function rstr2b64(input, b64pad) {
40893 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
40895 len = input.length,
40899 b64pad = b64pad || '=';
40901 for (i = 0; i < len; i += 3) {
40902 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
40904 for (j = 0; j < 4; j += 1) {
40905 if (i * 8 + j * 6 > input.length * 8) {
40908 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
40918 * @property {String} version
40928 Base64: function Base64() {
40929 // private properties
40930 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
40932 // URL encoding support @todo
40933 utf8 = true; // by default enable UTF-8 support encoding
40934 // public method for encoding
40936 this.encode = function (input) {
40941 len = input.length;
40943 input = utf8 ? utf8Encode(input) : input;
40945 for (i = 0; i < len; i += 3) {
40946 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
40948 for (j = 0; j < 4; j += 1) {
40949 if (i * 8 + j * 6 > len * 8) {
40952 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
40958 }; // public method for decoding
40961 this.decode = function (input) {
40962 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
40981 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
40985 // unpack four hexets into three octets using index points in b64
40986 h1 = tab.indexOf(input.charAt(i += 1));
40987 h2 = tab.indexOf(input.charAt(i += 1));
40988 h3 = tab.indexOf(input.charAt(i += 1));
40989 h4 = tab.indexOf(input.charAt(i += 1));
40990 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
40991 o1 = bits >> 16 & 0xff;
40992 o2 = bits >> 8 & 0xff;
40997 arr[ac] = String.fromCharCode(o1);
40998 } else if (h4 === 64) {
40999 arr[ac] = String.fromCharCode(o1, o2);
41001 arr[ac] = String.fromCharCode(o1, o2, o3);
41003 } while (i < input.length);
41005 dec = arr.join('');
41006 dec = utf8 ? utf8Decode(dec) : dec;
41008 }; // set custom pad string
41011 this.setPad = function (str) {
41014 }; // set custom tab string characters
41017 this.setTab = function (str) {
41022 this.setUTF8 = function (bool) {
41023 if (typeof bool === 'boolean') {
41032 * CRC-32 calculation
41036 * @param {String} str Input String
41039 CRC32: function CRC32(str) {
41046 str = utf8Encode(str);
41047 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('');
41050 for (i = 0, iTop = str.length; i < iTop; i += 1) {
41051 y = (crc ^ str.charCodeAt(i)) & 0xFF;
41052 x = '0x' + table.substr(y * 9, 8);
41053 crc = crc >>> 8 ^ x;
41054 } // always return a positive number (that's what >>> 0 does)
41057 return (crc ^ -1) >>> 0;
41064 * @param {Object} [config]
41066 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
41067 * Digest Algorithm, as defined in RFC 1321.
41068 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
41069 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41070 * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
41072 MD5: function MD5(options) {
41074 * Private config properties. You may need to tweak these to be compatible with
41075 * the server-side, but the defaults work in most cases.
41076 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
41078 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41079 // hexadecimal output case format. false - lowercase; true - uppercase
41080 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41081 // base-64 pad character. Defaults to '=' for strict RFC compliance
41082 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
41083 // privileged (public) methods
41085 this.hex = function (s) {
41086 return rstr2hex(rstr(s), hexcase);
41089 this.b64 = function (s) {
41090 return rstr2b64(rstr(s), b64pad);
41093 this.any = function (s, e) {
41094 return rstr2any(rstr(s), e);
41097 this.raw = function (s) {
41101 this.hex_hmac = function (k, d) {
41102 return rstr2hex(rstr_hmac(k, d), hexcase);
41105 this.b64_hmac = function (k, d) {
41106 return rstr2b64(rstr_hmac(k, d), b64pad);
41109 this.any_hmac = function (k, d, e) {
41110 return rstr2any(rstr_hmac(k, d), e);
41113 * Perform a simple self-test to see if the VM is working
41114 * @return {String} Hexadecimal hash sample
41118 this.vm_test = function () {
41119 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41122 * Enable/disable uppercase hexadecimal returned string
41124 * @return {Object} this
41128 this.setUpperCase = function (a) {
41129 if (typeof a === 'boolean') {
41136 * Defines a base64 pad string
41137 * @param {String} Pad
41138 * @return {Object} this
41142 this.setPad = function (a) {
41143 b64pad = a || b64pad;
41147 * Defines a base64 pad string
41149 * @return {Object} [this]
41153 this.setUTF8 = function (a) {
41154 if (typeof a === 'boolean') {
41159 }; // private methods
41162 * Calculate the MD5 of a raw string
41167 s = utf8 ? utf8Encode(s) : s;
41168 return binl2rstr(binl(rstr2binl(s), s.length * 8));
41171 * Calculate the HMAC-MD5, of a key and some data (raw strings)
41175 function rstr_hmac(key, data) {
41176 var bkey, ipad, opad, hash, i;
41177 key = utf8 ? utf8Encode(key) : key;
41178 data = utf8 ? utf8Encode(data) : data;
41179 bkey = rstr2binl(key);
41181 if (bkey.length > 16) {
41182 bkey = binl(bkey, key.length * 8);
41185 ipad = Array(16), opad = Array(16);
41187 for (i = 0; i < 16; i += 1) {
41188 ipad[i] = bkey[i] ^ 0x36363636;
41189 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41192 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
41193 return binl2rstr(binl(opad.concat(hash), 512 + 128));
41196 * Calculate the MD5 of an array of little-endian words, and a bit length.
41200 function binl(x, len) {
41210 /* append padding */
41212 x[len >> 5] |= 0x80 << len % 32;
41213 x[(len + 64 >>> 9 << 4) + 14] = len;
41215 for (i = 0; i < x.length; i += 16) {
41220 a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
41221 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
41222 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
41223 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
41224 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
41225 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
41226 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
41227 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
41228 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
41229 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
41230 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
41231 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
41232 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
41233 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
41234 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
41235 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
41236 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
41237 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
41238 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
41239 b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
41240 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
41241 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
41242 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
41243 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
41244 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
41245 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
41246 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
41247 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
41248 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
41249 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
41250 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
41251 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
41252 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
41253 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
41254 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
41255 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
41256 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
41257 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
41258 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
41259 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
41260 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
41261 d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
41262 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
41263 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
41264 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
41265 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
41266 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
41267 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
41268 a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
41269 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
41270 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
41271 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
41272 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
41273 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
41274 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
41275 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
41276 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
41277 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
41278 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
41279 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
41280 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
41281 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
41282 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
41283 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
41284 a = safe_add(a, olda);
41285 b = safe_add(b, oldb);
41286 c = safe_add(c, oldc);
41287 d = safe_add(d, oldd);
41290 return Array(a, b, c, d);
41293 * These functions implement the four basic operations the algorithm uses.
41297 function md5_cmn(q, a, b, x, s, t) {
41298 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
41301 function md5_ff(a, b, c, d, x, s, t) {
41302 return md5_cmn(b & c | ~b & d, a, b, x, s, t);
41305 function md5_gg(a, b, c, d, x, s, t) {
41306 return md5_cmn(b & d | c & ~d, a, b, x, s, t);
41309 function md5_hh(a, b, c, d, x, s, t) {
41310 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
41313 function md5_ii(a, b, c, d, x, s, t) {
41314 return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
41320 * @class Hashes.SHA1
41321 * @param {Object} [config]
41324 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
41325 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
41326 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41327 * See http://pajhome.org.uk/crypt/md5 for details.
41329 SHA1: function SHA1(options) {
41331 * Private config properties. You may need to tweak these to be compatible with
41332 * the server-side, but the defaults work in most cases.
41333 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
41335 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41336 // hexadecimal output case format. false - lowercase; true - uppercase
41337 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41338 // base-64 pad character. Defaults to '=' for strict RFC compliance
41339 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
41342 this.hex = function (s) {
41343 return rstr2hex(rstr(s), hexcase);
41346 this.b64 = function (s) {
41347 return rstr2b64(rstr(s), b64pad);
41350 this.any = function (s, e) {
41351 return rstr2any(rstr(s), e);
41354 this.raw = function (s) {
41358 this.hex_hmac = function (k, d) {
41359 return rstr2hex(rstr_hmac(k, d));
41362 this.b64_hmac = function (k, d) {
41363 return rstr2b64(rstr_hmac(k, d), b64pad);
41366 this.any_hmac = function (k, d, e) {
41367 return rstr2any(rstr_hmac(k, d), e);
41370 * Perform a simple self-test to see if the VM is working
41371 * @return {String} Hexadecimal hash sample
41376 this.vm_test = function () {
41377 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41380 * @description Enable/disable uppercase hexadecimal returned string
41382 * @return {Object} this
41387 this.setUpperCase = function (a) {
41388 if (typeof a === 'boolean') {
41395 * @description Defines a base64 pad string
41396 * @param {string} Pad
41397 * @return {Object} this
41402 this.setPad = function (a) {
41403 b64pad = a || b64pad;
41407 * @description Defines a base64 pad string
41409 * @return {Object} this
41414 this.setUTF8 = function (a) {
41415 if (typeof a === 'boolean') {
41420 }; // private methods
41423 * Calculate the SHA-512 of a raw string
41428 s = utf8 ? utf8Encode(s) : s;
41429 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41432 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
41436 function rstr_hmac(key, data) {
41437 var bkey, ipad, opad, i, hash;
41438 key = utf8 ? utf8Encode(key) : key;
41439 data = utf8 ? utf8Encode(data) : data;
41440 bkey = rstr2binb(key);
41442 if (bkey.length > 16) {
41443 bkey = binb(bkey, key.length * 8);
41446 ipad = Array(16), opad = Array(16);
41448 for (i = 0; i < 16; i += 1) {
41449 ipad[i] = bkey[i] ^ 0x36363636;
41450 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41453 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
41454 return binb2rstr(binb(opad.concat(hash), 512 + 160));
41457 * Calculate the SHA-1 of an array of big-endian words, and a bit length
41461 function binb(x, len) {
41476 /* append padding */
41478 x[len >> 5] |= 0x80 << 24 - len % 32;
41479 x[(len + 64 >> 9 << 4) + 15] = len;
41481 for (i = 0; i < x.length; i += 16) {
41488 for (j = 0; j < 80; j += 1) {
41492 w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
41495 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)));
41498 c = bit_rol(b, 30);
41503 a = safe_add(a, olda);
41504 b = safe_add(b, oldb);
41505 c = safe_add(c, oldc);
41506 d = safe_add(d, oldd);
41507 e = safe_add(e, olde);
41510 return Array(a, b, c, d, e);
41513 * Perform the appropriate triplet combination function for the current
41518 function sha1_ft(t, b, c, d) {
41520 return b & c | ~b & d;
41528 return b & c | b & d | c & d;
41534 * Determine the appropriate additive constant for the current iteration
41538 function sha1_kt(t) {
41539 return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
41544 * @class Hashes.SHA256
41547 * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
41548 * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
41549 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41550 * See http://pajhome.org.uk/crypt/md5 for details.
41551 * Also http://anmar.eu.org/projects/jssha2/
41553 SHA256: function SHA256(options) {
41555 * Private properties configuration variables. You may need to tweak these to be compatible with
41556 * the server-side, but the defaults work in most cases.
41557 * @see this.setUpperCase() method
41558 * @see this.setPad() method
41560 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41561 // hexadecimal output case format. false - lowercase; true - uppercase */
41562 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41564 /* base-64 pad character. Default '=' for strict RFC compliance */
41565 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
41567 /* enable/disable utf8 encoding */
41569 /* privileged (public) methods */
41571 this.hex = function (s) {
41572 return rstr2hex(rstr(s, utf8));
41575 this.b64 = function (s) {
41576 return rstr2b64(rstr(s, utf8), b64pad);
41579 this.any = function (s, e) {
41580 return rstr2any(rstr(s, utf8), e);
41583 this.raw = function (s) {
41584 return rstr(s, utf8);
41587 this.hex_hmac = function (k, d) {
41588 return rstr2hex(rstr_hmac(k, d));
41591 this.b64_hmac = function (k, d) {
41592 return rstr2b64(rstr_hmac(k, d), b64pad);
41595 this.any_hmac = function (k, d, e) {
41596 return rstr2any(rstr_hmac(k, d), e);
41599 * Perform a simple self-test to see if the VM is working
41600 * @return {String} Hexadecimal hash sample
41605 this.vm_test = function () {
41606 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41609 * Enable/disable uppercase hexadecimal returned string
41611 * @return {Object} this
41616 this.setUpperCase = function (a) {
41617 if (typeof a === 'boolean') {
41624 * @description Defines a base64 pad string
41625 * @param {string} Pad
41626 * @return {Object} this
41631 this.setPad = function (a) {
41632 b64pad = a || b64pad;
41636 * Defines a base64 pad string
41638 * @return {Object} this
41643 this.setUTF8 = function (a) {
41644 if (typeof a === 'boolean') {
41649 }; // private methods
41652 * Calculate the SHA-512 of a raw string
41656 function rstr(s, utf8) {
41657 s = utf8 ? utf8Encode(s) : s;
41658 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41661 * Calculate the HMAC-sha256 of a key and some data (raw strings)
41665 function rstr_hmac(key, data) {
41666 key = utf8 ? utf8Encode(key) : key;
41667 data = utf8 ? utf8Encode(data) : data;
41670 bkey = rstr2binb(key),
41674 if (bkey.length > 16) {
41675 bkey = binb(bkey, key.length * 8);
41678 for (; i < 16; i += 1) {
41679 ipad[i] = bkey[i] ^ 0x36363636;
41680 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41683 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
41684 return binb2rstr(binb(opad.concat(hash), 512 + 256));
41687 * Main sha256 function, with its support functions
41691 function sha256_S(X, n) {
41692 return X >>> n | X << 32 - n;
41695 function sha256_R(X, n) {
41699 function sha256_Ch(x, y, z) {
41700 return x & y ^ ~x & z;
41703 function sha256_Maj(x, y, z) {
41704 return x & y ^ x & z ^ y & z;
41707 function sha256_Sigma0256(x) {
41708 return sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22);
41711 function sha256_Sigma1256(x) {
41712 return sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25);
41715 function sha256_Gamma0256(x) {
41716 return sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3);
41719 function sha256_Gamma1256(x) {
41720 return sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10);
41723 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];
41725 function binb(m, l) {
41726 var HASH = [1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225];
41727 var W = new Array(64);
41728 var a, b, c, d, e, f, g, h;
41730 /* append padding */
41732 m[l >> 5] |= 0x80 << 24 - l % 32;
41733 m[(l + 64 >> 9 << 4) + 15] = l;
41735 for (i = 0; i < m.length; i += 16) {
41745 for (j = 0; j < 64; j += 1) {
41749 W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), sha256_Gamma0256(W[j - 15])), W[j - 16]);
41752 T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), sha256_K[j]), W[j]);
41753 T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
41757 e = safe_add(d, T1);
41761 a = safe_add(T1, T2);
41764 HASH[0] = safe_add(a, HASH[0]);
41765 HASH[1] = safe_add(b, HASH[1]);
41766 HASH[2] = safe_add(c, HASH[2]);
41767 HASH[3] = safe_add(d, HASH[3]);
41768 HASH[4] = safe_add(e, HASH[4]);
41769 HASH[5] = safe_add(f, HASH[5]);
41770 HASH[6] = safe_add(g, HASH[6]);
41771 HASH[7] = safe_add(h, HASH[7]);
41779 * @class Hashes.SHA512
41782 * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
41783 * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
41784 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41785 * See http://pajhome.org.uk/crypt/md5 for details.
41787 SHA512: function SHA512(options) {
41789 * Private properties configuration variables. You may need to tweak these to be compatible with
41790 * the server-side, but the defaults work in most cases.
41791 * @see this.setUpperCase() method
41792 * @see this.setPad() method
41794 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41796 /* hexadecimal output case format. false - lowercase; true - uppercase */
41797 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41799 /* base-64 pad character. Default '=' for strict RFC compliance */
41800 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
41802 /* enable/disable utf8 encoding */
41804 /* privileged (public) methods */
41806 this.hex = function (s) {
41807 return rstr2hex(rstr(s));
41810 this.b64 = function (s) {
41811 return rstr2b64(rstr(s), b64pad);
41814 this.any = function (s, e) {
41815 return rstr2any(rstr(s), e);
41818 this.raw = function (s) {
41822 this.hex_hmac = function (k, d) {
41823 return rstr2hex(rstr_hmac(k, d));
41826 this.b64_hmac = function (k, d) {
41827 return rstr2b64(rstr_hmac(k, d), b64pad);
41830 this.any_hmac = function (k, d, e) {
41831 return rstr2any(rstr_hmac(k, d), e);
41834 * Perform a simple self-test to see if the VM is working
41835 * @return {String} Hexadecimal hash sample
41840 this.vm_test = function () {
41841 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41844 * @description Enable/disable uppercase hexadecimal returned string
41846 * @return {Object} this
41851 this.setUpperCase = function (a) {
41852 if (typeof a === 'boolean') {
41859 * @description Defines a base64 pad string
41860 * @param {string} Pad
41861 * @return {Object} this
41866 this.setPad = function (a) {
41867 b64pad = a || b64pad;
41871 * @description Defines a base64 pad string
41873 * @return {Object} this
41878 this.setUTF8 = function (a) {
41879 if (typeof a === 'boolean') {
41885 /* private methods */
41888 * Calculate the SHA-512 of a raw string
41893 s = utf8 ? utf8Encode(s) : s;
41894 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41897 * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
41901 function rstr_hmac(key, data) {
41902 key = utf8 ? utf8Encode(key) : key;
41903 data = utf8 ? utf8Encode(data) : data;
41906 bkey = rstr2binb(key),
41910 if (bkey.length > 32) {
41911 bkey = binb(bkey, key.length * 8);
41914 for (; i < 32; i += 1) {
41915 ipad[i] = bkey[i] ^ 0x36363636;
41916 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41919 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
41920 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
41923 * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
41927 function binb(x, len) {
41932 hash = new Array(16),
41933 //Initial hash values
41934 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)],
41935 T1 = new int64(0, 0),
41936 T2 = new int64(0, 0),
41937 a = new int64(0, 0),
41938 b = new int64(0, 0),
41939 c = new int64(0, 0),
41940 d = new int64(0, 0),
41941 e = new int64(0, 0),
41942 f = new int64(0, 0),
41943 g = new int64(0, 0),
41944 h = new int64(0, 0),
41945 //Temporary variables not specified by the document
41946 s0 = new int64(0, 0),
41947 s1 = new int64(0, 0),
41948 Ch = new int64(0, 0),
41949 Maj = new int64(0, 0),
41950 r1 = new int64(0, 0),
41951 r2 = new int64(0, 0),
41952 r3 = new int64(0, 0);
41954 if (sha512_k === undefined) {
41956 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)];
41959 for (i = 0; i < 80; i += 1) {
41960 W[i] = new int64(0, 0);
41961 } // append padding to the source string. The format is described in the FIPS.
41964 x[len >> 5] |= 0x80 << 24 - (len & 0x1f);
41965 x[(len + 128 >> 10 << 5) + 31] = len;
41968 for (i = 0; i < l; i += 32) {
41969 //32 dwords is the block size
41970 int64copy(a, H[0]);
41971 int64copy(b, H[1]);
41972 int64copy(c, H[2]);
41973 int64copy(d, H[3]);
41974 int64copy(e, H[4]);
41975 int64copy(f, H[5]);
41976 int64copy(g, H[6]);
41977 int64copy(h, H[7]);
41979 for (j = 0; j < 16; j += 1) {
41980 W[j].h = x[i + 2 * j];
41981 W[j].l = x[i + 2 * j + 1];
41984 for (j = 16; j < 80; j += 1) {
41986 int64rrot(r1, W[j - 2], 19);
41987 int64revrrot(r2, W[j - 2], 29);
41988 int64shr(r3, W[j - 2], 6);
41989 s1.l = r1.l ^ r2.l ^ r3.l;
41990 s1.h = r1.h ^ r2.h ^ r3.h; //sigma0
41992 int64rrot(r1, W[j - 15], 1);
41993 int64rrot(r2, W[j - 15], 8);
41994 int64shr(r3, W[j - 15], 7);
41995 s0.l = r1.l ^ r2.l ^ r3.l;
41996 s0.h = r1.h ^ r2.h ^ r3.h;
41997 int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
42000 for (j = 0; j < 80; j += 1) {
42002 Ch.l = e.l & f.l ^ ~e.l & g.l;
42003 Ch.h = e.h & f.h ^ ~e.h & g.h; //Sigma1
42005 int64rrot(r1, e, 14);
42006 int64rrot(r2, e, 18);
42007 int64revrrot(r3, e, 9);
42008 s1.l = r1.l ^ r2.l ^ r3.l;
42009 s1.h = r1.h ^ r2.h ^ r3.h; //Sigma0
42011 int64rrot(r1, a, 28);
42012 int64revrrot(r2, a, 2);
42013 int64revrrot(r3, a, 7);
42014 s0.l = r1.l ^ r2.l ^ r3.l;
42015 s0.h = r1.h ^ r2.h ^ r3.h; //Maj
42017 Maj.l = a.l & b.l ^ a.l & c.l ^ b.l & c.l;
42018 Maj.h = a.h & b.h ^ a.h & c.h ^ b.h & c.h;
42019 int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
42020 int64add(T2, s0, Maj);
42024 int64add(e, d, T1);
42028 int64add(a, T1, T2);
42031 int64add(H[0], H[0], a);
42032 int64add(H[1], H[1], b);
42033 int64add(H[2], H[2], c);
42034 int64add(H[3], H[3], d);
42035 int64add(H[4], H[4], e);
42036 int64add(H[5], H[5], f);
42037 int64add(H[6], H[6], g);
42038 int64add(H[7], H[7], h);
42039 } //represent the hash as an array of 32-bit dwords
42042 for (i = 0; i < 8; i += 1) {
42043 hash[2 * i] = H[i].h;
42044 hash[2 * i + 1] = H[i].l;
42048 } //A constructor for 64-bit numbers
42051 function int64(h, l) {
42053 this.l = l; //this.toString = int64toString;
42054 } //Copies src into dst, assuming both are 64-bit numbers
42057 function int64copy(dst, src) {
42060 } //Right-rotates a 64-bit number by shift
42061 //Won't handle cases of shift>=32
42062 //The function revrrot() is for that
42065 function int64rrot(dst, x, shift) {
42066 dst.l = x.l >>> shift | x.h << 32 - shift;
42067 dst.h = x.h >>> shift | x.l << 32 - shift;
42068 } //Reverses the dwords of the source and then rotates right by shift.
42069 //This is equivalent to rotation by 32+shift
42072 function int64revrrot(dst, x, shift) {
42073 dst.l = x.h >>> shift | x.l << 32 - shift;
42074 dst.h = x.l >>> shift | x.h << 32 - shift;
42075 } //Bitwise-shifts right a 64-bit number by shift
42076 //Won't handle shift>=32, but it's never needed in SHA512
42079 function int64shr(dst, x, shift) {
42080 dst.l = x.l >>> shift | x.h << 32 - shift;
42081 dst.h = x.h >>> shift;
42082 } //Adds two 64-bit numbers
42083 //Like the original implementation, does not rely on 32-bit operations
42086 function int64add(dst, x, y) {
42087 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
42088 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
42089 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
42090 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
42091 dst.l = w0 & 0xffff | w1 << 16;
42092 dst.h = w2 & 0xffff | w3 << 16;
42093 } //Same, except with 4 addends. Works faster than adding them one by one.
42096 function int64add4(dst, a, b, c, d) {
42097 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
42098 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
42099 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
42100 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
42101 dst.l = w0 & 0xffff | w1 << 16;
42102 dst.h = w2 & 0xffff | w3 << 16;
42103 } //Same, except with 5 addends
42106 function int64add5(dst, a, b, c, d, e) {
42107 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
42108 w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
42109 w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
42110 w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
42111 dst.l = w0 & 0xffff | w1 << 16;
42112 dst.h = w2 & 0xffff | w3 << 16;
42117 * @class Hashes.RMD160
42119 * @param {Object} [config]
42121 * A JavaScript implementation of the RIPEMD-160 Algorithm
42122 * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
42123 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
42124 * See http://pajhome.org.uk/crypt/md5 for details.
42125 * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
42127 RMD160: function RMD160(options) {
42129 * Private properties configuration variables. You may need to tweak these to be compatible with
42130 * the server-side, but the defaults work in most cases.
42131 * @see this.setUpperCase() method
42132 * @see this.setPad() method
42134 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
42136 /* hexadecimal output case format. false - lowercase; true - uppercase */
42137 b64pad = options && typeof options.pad === 'string' ? options.pa : '=',
42139 /* base-64 pad character. Default '=' for strict RFC compliance */
42140 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
42142 /* enable/disable utf8 encoding */
42143 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],
42144 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],
42145 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],
42146 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];
42147 /* privileged (public) methods */
42149 this.hex = function (s) {
42150 return rstr2hex(rstr(s));
42153 this.b64 = function (s) {
42154 return rstr2b64(rstr(s), b64pad);
42157 this.any = function (s, e) {
42158 return rstr2any(rstr(s), e);
42161 this.raw = function (s) {
42165 this.hex_hmac = function (k, d) {
42166 return rstr2hex(rstr_hmac(k, d));
42169 this.b64_hmac = function (k, d) {
42170 return rstr2b64(rstr_hmac(k, d), b64pad);
42173 this.any_hmac = function (k, d, e) {
42174 return rstr2any(rstr_hmac(k, d), e);
42177 * Perform a simple self-test to see if the VM is working
42178 * @return {String} Hexadecimal hash sample
42183 this.vm_test = function () {
42184 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
42187 * @description Enable/disable uppercase hexadecimal returned string
42189 * @return {Object} this
42194 this.setUpperCase = function (a) {
42195 if (typeof a === 'boolean') {
42202 * @description Defines a base64 pad string
42203 * @param {string} Pad
42204 * @return {Object} this
42209 this.setPad = function (a) {
42210 if (typeof a !== 'undefined') {
42217 * @description Defines a base64 pad string
42219 * @return {Object} this
42224 this.setUTF8 = function (a) {
42225 if (typeof a === 'boolean') {
42231 /* private methods */
42234 * Calculate the rmd160 of a raw string
42239 s = utf8 ? utf8Encode(s) : s;
42240 return binl2rstr(binl(rstr2binl(s), s.length * 8));
42243 * Calculate the HMAC-rmd160 of a key and some data (raw strings)
42247 function rstr_hmac(key, data) {
42248 key = utf8 ? utf8Encode(key) : key;
42249 data = utf8 ? utf8Encode(data) : data;
42252 bkey = rstr2binl(key),
42256 if (bkey.length > 16) {
42257 bkey = binl(bkey, key.length * 8);
42260 for (i = 0; i < 16; i += 1) {
42261 ipad[i] = bkey[i] ^ 0x36363636;
42262 opad[i] = bkey[i] ^ 0x5C5C5C5C;
42265 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
42266 return binl2rstr(binl(opad.concat(hash), 512 + 160));
42269 * Convert an array of little-endian words to a string
42273 function binl2rstr(input) {
42276 l = input.length * 32;
42278 for (i = 0; i < l; i += 8) {
42279 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
42285 * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
42289 function binl(x, len) {
42309 /* append padding */
42311 x[len >> 5] |= 0x80 << len % 32;
42312 x[(len + 64 >>> 9 << 4) + 14] = len;
42315 for (i = 0; i < l; i += 16) {
42322 for (j = 0; j <= 79; j += 1) {
42323 T = safe_add(A1, rmd160_f(j, B1, C1, D1));
42324 T = safe_add(T, x[i + rmd160_r1[j]]);
42325 T = safe_add(T, rmd160_K1(j));
42326 T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
42329 D1 = bit_rol(C1, 10);
42332 T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
42333 T = safe_add(T, x[i + rmd160_r2[j]]);
42334 T = safe_add(T, rmd160_K2(j));
42335 T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
42338 D2 = bit_rol(C2, 10);
42343 T = safe_add(h1, safe_add(C1, D2));
42344 h1 = safe_add(h2, safe_add(D1, E2));
42345 h2 = safe_add(h3, safe_add(E1, A2));
42346 h3 = safe_add(h4, safe_add(A1, B2));
42347 h4 = safe_add(h0, safe_add(B1, C2));
42351 return [h0, h1, h2, h3, h4];
42352 } // specific algorithm methods
42355 function rmd160_f(j, x, y, z) {
42356 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';
42359 function rmd160_K1(j) {
42360 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';
42363 function rmd160_K2(j) {
42364 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';
42367 }; // exposes Hashes
42369 (function (window, undefined$1) {
42370 var freeExports = false;
42373 freeExports = exports;
42375 if (exports && _typeof(commonjsGlobal) === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
42376 window = commonjsGlobal;
42380 if (typeof undefined$1 === 'function' && _typeof(undefined$1.amd) === 'object' && undefined$1.amd) {
42381 // define as an anonymous module, so, through path mapping, it can be aliased
42382 undefined$1(function () {
42385 } else if (freeExports) {
42386 // in Node.js or RingoJS v0.8.0+
42387 if ( module && module.exports === freeExports) {
42388 module.exports = Hashes;
42389 } // in Narwhal or RingoJS v0.7.0-
42391 freeExports.Hashes = Hashes;
42394 // in a browser or Rhino
42395 window.Hashes = Hashes;
42402 var immutable = extend$2;
42403 var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
42405 function extend$2() {
42408 for (var i = 0; i < arguments.length; i++) {
42409 var source = arguments[i];
42411 for (var key in source) {
42412 if (hasOwnProperty$2.call(source, key)) {
42413 target[key] = source[key];
42421 var sha1 = new hashes.SHA1();
42424 ohauth.qsString = function (obj) {
42425 return Object.keys(obj).sort().map(function (key) {
42426 return ohauth.percentEncode(key) + '=' + ohauth.percentEncode(obj[key]);
42430 ohauth.stringQs = function (str) {
42431 return str.split('&').filter(function (pair) {
42432 return pair !== '';
42433 }).reduce(function (obj, pair) {
42434 var parts = pair.split('=');
42435 obj[decodeURIComponent(parts[0])] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
42440 ohauth.rawxhr = function (method, url, data, headers, callback) {
42441 var xhr = new XMLHttpRequest(),
42442 twoHundred = /^20\d$/;
42444 xhr.onreadystatechange = function () {
42445 if (4 === xhr.readyState && 0 !== xhr.status) {
42446 if (twoHundred.test(xhr.status)) callback(null, xhr);else return callback(xhr, null);
42450 xhr.onerror = function (e) {
42451 return callback(e, null);
42454 xhr.open(method, url, true);
42456 for (var h in headers) {
42457 xhr.setRequestHeader(h, headers[h]);
42464 ohauth.xhr = function (method, url, auth, data, options, callback) {
42465 var headers = options && options.header || {
42466 'Content-Type': 'application/x-www-form-urlencoded'
42468 headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
42469 return ohauth.rawxhr(method, url, data, headers, callback);
42472 ohauth.nonce = function () {
42473 for (var o = ''; o.length < 6;) {
42474 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
42480 ohauth.authHeader = function (obj) {
42481 return Object.keys(obj).sort().map(function (key) {
42482 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
42486 ohauth.timestamp = function () {
42487 return ~~(+new Date() / 1000);
42490 ohauth.percentEncode = function (s) {
42491 return encodeURIComponent(s).replace(/\!/g, '%21').replace(/\'/g, '%27').replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
42494 ohauth.baseString = function (method, url, params) {
42495 if (params.oauth_signature) delete params.oauth_signature;
42496 return [method, ohauth.percentEncode(url), ohauth.percentEncode(ohauth.qsString(params))].join('&');
42499 ohauth.signature = function (oauth_secret, token_secret, baseString) {
42500 return sha1.b64_hmac(ohauth.percentEncode(oauth_secret) + '&' + ohauth.percentEncode(token_secret), baseString);
42503 * Takes an options object for configuration (consumer_key,
42504 * consumer_secret, version, signature_method, token, token_secret)
42505 * and returns a function that generates the Authorization header
42508 * The returned function takes these parameters:
42509 * - method: GET/POST/...
42510 * - uri: full URI with protocol, port, path and query string
42511 * - extra_params: any extra parameters (that are passed in the POST data),
42512 * can be an object or a from-urlencoded string.
42514 * Returned function returns full OAuth header with "OAuth" string in it.
42518 ohauth.headerGenerator = function (options) {
42519 options = options || {};
42520 var consumer_key = options.consumer_key || '',
42521 consumer_secret = options.consumer_secret || '',
42522 signature_method = options.signature_method || 'HMAC-SHA1',
42523 version = options.version || '1.0',
42524 token = options.token || '',
42525 token_secret = options.token_secret || '';
42526 return function (method, uri, extra_params) {
42527 method = method.toUpperCase();
42529 if (typeof extra_params === 'string' && extra_params.length > 0) {
42530 extra_params = ohauth.stringQs(extra_params);
42533 var uri_parts = uri.split('?', 2),
42534 base_uri = uri_parts[0];
42535 var query_params = uri_parts.length === 2 ? ohauth.stringQs(uri_parts[1]) : {};
42536 var oauth_params = {
42537 oauth_consumer_key: consumer_key,
42538 oauth_signature_method: signature_method,
42539 oauth_version: version,
42540 oauth_timestamp: ohauth.timestamp(),
42541 oauth_nonce: ohauth.nonce()
42543 if (token) oauth_params.oauth_token = token;
42544 var all_params = immutable({}, oauth_params, query_params, extra_params),
42545 base_str = ohauth.baseString(method, base_uri, all_params);
42546 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
42547 return 'OAuth ' + ohauth.authHeader(oauth_params);
42551 var ohauth_1 = ohauth;
42553 var resolveUrl$1 = createCommonjsModule(function (module, exports) {
42554 // Copyright 2014 Simon Lydell
42555 // X11 (“MIT”) Licensed. (See LICENSE.)
42556 void function (root, factory) {
42558 module.exports = factory();
42560 }(commonjsGlobal, function () {
42561 function resolveUrl()
42564 var numUrls = arguments.length;
42566 if (numUrls === 0) {
42567 throw new Error("resolveUrl requires at least one argument; got none.");
42570 var base = document.createElement("base");
42571 base.href = arguments[0];
42573 if (numUrls === 1) {
42577 var head = document.getElementsByTagName("head")[0];
42578 head.insertBefore(base, head.firstChild);
42579 var a = document.createElement("a");
42582 for (var index = 1; index < numUrls; index++) {
42583 a.href = arguments[index];
42585 base.href = resolved;
42588 head.removeChild(base);
42596 var assign = make_assign();
42597 var create$1 = make_create();
42598 var trim$3 = make_trim();
42599 var Global = typeof window !== 'undefined' ? window : commonjsGlobal;
42610 isFunction: isFunction,
42611 isObject: isObject$2,
42615 function make_assign() {
42616 if (Object.assign) {
42617 return Object.assign;
42619 return function shimAssign(obj, props1, props2, etc) {
42620 for (var i = 1; i < arguments.length; i++) {
42621 each(Object(arguments[i]), function (val, key) {
42631 function make_create() {
42632 if (Object.create) {
42633 return function create(obj, assignProps1, assignProps2, etc) {
42634 var assignArgsList = slice$2(arguments, 1);
42635 return assign.apply(this, [Object.create(obj)].concat(assignArgsList));
42638 var F = function F() {}; // eslint-disable-line no-inner-declarations
42641 return function create(obj, assignProps1, assignProps2, etc) {
42642 var assignArgsList = slice$2(arguments, 1);
42644 return assign.apply(this, [new F()].concat(assignArgsList));
42649 function make_trim() {
42650 if (String.prototype.trim) {
42651 return function trim(str) {
42652 return String.prototype.trim.call(str);
42655 return function trim(str) {
42656 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
42661 function bind$1(obj, fn) {
42662 return function () {
42663 return fn.apply(obj, Array.prototype.slice.call(arguments, 0));
42667 function slice$2(arr, index) {
42668 return Array.prototype.slice.call(arr, index || 0);
42671 function each(obj, fn) {
42672 pluck(obj, function (val, key) {
42678 function map$1(obj, fn) {
42679 var res = isList(obj) ? [] : {};
42680 pluck(obj, function (v, k) {
42687 function pluck(obj, fn) {
42689 for (var i = 0; i < obj.length; i++) {
42690 if (fn(obj[i], i)) {
42695 for (var key in obj) {
42696 if (obj.hasOwnProperty(key)) {
42697 if (fn(obj[key], key)) {
42705 function isList(val) {
42706 return val != null && typeof val != 'function' && typeof val.length == 'number';
42709 function isFunction(val) {
42710 return val && {}.toString.call(val) === '[object Function]';
42713 function isObject$2(val) {
42714 return val && {}.toString.call(val) === '[object Object]';
42717 var slice$3 = util.slice;
42718 var pluck$1 = util.pluck;
42719 var each$1 = util.each;
42720 var bind$2 = util.bind;
42721 var create$2 = util.create;
42722 var isList$1 = util.isList;
42723 var isFunction$1 = util.isFunction;
42724 var isObject$3 = util.isObject;
42725 var storeEngine = {
42726 createStore: _createStore
42731 // get returns the value of the given key. If that value
42732 // is undefined, it returns optionalDefaultValue instead.
42733 get: function get(key, optionalDefaultValue) {
42734 var data = this.storage.read(this._namespacePrefix + key);
42735 return this._deserialize(data, optionalDefaultValue);
42737 // set will store the given value at key and returns value.
42738 // Calling set with value === undefined is equivalent to calling remove.
42739 set: function set(key, value) {
42740 if (value === undefined) {
42741 return this.remove(key);
42744 this.storage.write(this._namespacePrefix + key, this._serialize(value));
42747 // remove deletes the key and value stored at the given key.
42748 remove: function remove(key) {
42749 this.storage.remove(this._namespacePrefix + key);
42751 // each will call the given callback once for each key-value pair
42753 each: function each(callback) {
42755 this.storage.each(function (val, namespacedKey) {
42756 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
42759 // clearAll will remove all the stored key-value pairs in this store.
42760 clearAll: function clearAll() {
42761 this.storage.clearAll();
42763 // additional functionality that can't live in plugins
42764 // ---------------------------------------------------
42765 // hasNamespace returns true if this store instance has the given namespace.
42766 hasNamespace: function hasNamespace(namespace) {
42767 return this._namespacePrefix == '__storejs_' + namespace + '_';
42769 // createStore creates a store.js instance with the first
42770 // functioning storage in the list of storage candidates,
42771 // and applies the the given mixins to the instance.
42772 createStore: function createStore() {
42773 return _createStore.apply(this, arguments);
42775 addPlugin: function addPlugin(plugin) {
42776 this._addPlugin(plugin);
42778 namespace: function namespace(_namespace) {
42779 return _createStore(this.storage, this.plugins, _namespace);
42784 var _console = typeof console == 'undefined' ? null : console;
42790 var fn = _console.warn ? _console.warn : _console.log;
42791 fn.apply(_console, arguments);
42794 function _createStore(storages, plugins, namespace) {
42799 if (storages && !isList$1(storages)) {
42800 storages = [storages];
42803 if (plugins && !isList$1(plugins)) {
42804 plugins = [plugins];
42807 var namespacePrefix = namespace ? '__storejs_' + namespace + '_' : '';
42808 var namespaceRegexp = namespace ? new RegExp('^' + namespacePrefix) : null;
42809 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
42811 if (!legalNamespaces.test(namespace)) {
42812 throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes');
42815 var _privateStoreProps = {
42816 _namespacePrefix: namespacePrefix,
42817 _namespaceRegexp: namespaceRegexp,
42818 _testStorage: function _testStorage(storage) {
42820 var testStr = '__storejs__test__';
42821 storage.write(testStr, testStr);
42822 var ok = storage.read(testStr) === testStr;
42823 storage.remove(testStr);
42829 _assignPluginFnProp: function _assignPluginFnProp(pluginFnProp, propName) {
42830 var oldFn = this[propName];
42832 this[propName] = function pluginFn() {
42833 var args = slice$3(arguments, 0);
42834 var self = this; // super_fn calls the old function which was overwritten by
42837 function super_fn() {
42842 each$1(arguments, function (arg, i) {
42845 return oldFn.apply(self, args);
42846 } // Give mixing function access to super_fn by prefixing all mixin function
42847 // arguments with super_fn.
42850 var newFnArgs = [super_fn].concat(args);
42851 return pluginFnProp.apply(self, newFnArgs);
42854 _serialize: function _serialize(obj) {
42855 return JSON.stringify(obj);
42857 _deserialize: function _deserialize(strVal, defaultVal) {
42860 } // It is possible that a raw string value has been previously stored
42861 // in a storage without using store.js, meaning it will be a raw
42862 // string value instead of a JSON serialized string. By defaulting
42863 // to the raw string value in case of a JSON parse error, we allow
42864 // for past stored values to be forwards-compatible with store.js
42870 val = JSON.parse(strVal);
42875 return val !== undefined ? val : defaultVal;
42877 _addStorage: function _addStorage(storage) {
42878 if (this.enabled) {
42882 if (this._testStorage(storage)) {
42883 this.storage = storage;
42884 this.enabled = true;
42887 _addPlugin: function _addPlugin(plugin) {
42888 var self = this; // If the plugin is an array, then add all plugins in the array.
42889 // This allows for a plugin to depend on other plugins.
42891 if (isList$1(plugin)) {
42892 each$1(plugin, function (plugin) {
42893 self._addPlugin(plugin);
42896 } // Keep track of all plugins we've seen so far, so that we
42897 // don't add any of them twice.
42900 var seenPlugin = pluck$1(this.plugins, function (seenPlugin) {
42901 return plugin === seenPlugin;
42908 this.plugins.push(plugin); // Check that the plugin is properly formed
42910 if (!isFunction$1(plugin)) {
42911 throw new Error('Plugins must be function values that return objects');
42914 var pluginProperties = plugin.call(this);
42916 if (!isObject$3(pluginProperties)) {
42917 throw new Error('Plugins must return an object of function properties');
42918 } // Add the plugin function properties to this store instance.
42921 each$1(pluginProperties, function (pluginFnProp, propName) {
42922 if (!isFunction$1(pluginFnProp)) {
42923 throw new Error('Bad plugin property: ' + propName + ' from plugin ' + plugin.name + '. Plugins should only return functions.');
42926 self._assignPluginFnProp(pluginFnProp, propName);
42929 // Put deprecated properties in the private API, so as to not expose it to accidential
42930 // discovery through inspection of the store object.
42931 // Deprecated: addStorage
42932 addStorage: function addStorage(storage) {
42933 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
42935 this._addStorage(storage);
42938 var store = create$2(_privateStoreProps, storeAPI, {
42942 each$1(store, function (prop, propName) {
42943 if (isFunction$1(prop)) {
42944 store.raw[propName] = bind$2(store, prop);
42947 each$1(storages, function (storage) {
42948 store._addStorage(storage);
42950 each$1(plugins, function (plugin) {
42951 store._addPlugin(plugin);
42956 var Global$1 = util.Global;
42957 var localStorage_1 = {
42958 name: 'localStorage',
42966 function localStorage$1() {
42967 return Global$1.localStorage;
42970 function read(key) {
42971 return localStorage$1().getItem(key);
42974 function write(key, data) {
42975 return localStorage$1().setItem(key, data);
42978 function each$2(fn) {
42979 for (var i = localStorage$1().length - 1; i >= 0; i--) {
42980 var key = localStorage$1().key(i);
42981 fn(read(key), key);
42985 function remove$2(key) {
42986 return localStorage$1().removeItem(key);
42989 function clearAll() {
42990 return localStorage$1().clear();
42993 // versions 6 and 7, where no localStorage, etc
42996 var Global$2 = util.Global;
42997 var oldFFGlobalStorage = {
42998 name: 'oldFF-globalStorage',
43003 clearAll: clearAll$1
43005 var globalStorage = Global$2.globalStorage;
43007 function read$1(key) {
43008 return globalStorage[key];
43011 function write$1(key, data) {
43012 globalStorage[key] = data;
43015 function each$3(fn) {
43016 for (var i = globalStorage.length - 1; i >= 0; i--) {
43017 var key = globalStorage.key(i);
43018 fn(globalStorage[key], key);
43022 function remove$3(key) {
43023 return globalStorage.removeItem(key);
43026 function clearAll$1() {
43027 each$3(function (key, _) {
43028 delete globalStorage[key];
43032 // versions 6 and 7, where no localStorage, sessionStorage, etc
43035 var Global$3 = util.Global;
43036 var oldIEUserDataStorage = {
43037 name: 'oldIE-userDataStorage',
43042 clearAll: clearAll$2
43044 var storageName = 'storejs';
43045 var doc = Global$3.document;
43047 var _withStorageEl = _makeIEStorageElFunction();
43049 var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
43051 function write$2(unfixedKey, data) {
43056 var fixedKey = fixKey(unfixedKey);
43058 _withStorageEl(function (storageEl) {
43059 storageEl.setAttribute(fixedKey, data);
43060 storageEl.save(storageName);
43064 function read$2(unfixedKey) {
43069 var fixedKey = fixKey(unfixedKey);
43072 _withStorageEl(function (storageEl) {
43073 res = storageEl.getAttribute(fixedKey);
43079 function each$4(callback) {
43080 _withStorageEl(function (storageEl) {
43081 var attributes = storageEl.XMLDocument.documentElement.attributes;
43083 for (var i = attributes.length - 1; i >= 0; i--) {
43084 var attr = attributes[i];
43085 callback(storageEl.getAttribute(attr.name), attr.name);
43090 function remove$4(unfixedKey) {
43091 var fixedKey = fixKey(unfixedKey);
43093 _withStorageEl(function (storageEl) {
43094 storageEl.removeAttribute(fixedKey);
43095 storageEl.save(storageName);
43099 function clearAll$2() {
43100 _withStorageEl(function (storageEl) {
43101 var attributes = storageEl.XMLDocument.documentElement.attributes;
43102 storageEl.load(storageName);
43104 for (var i = attributes.length - 1; i >= 0; i--) {
43105 storageEl.removeAttribute(attributes[i].name);
43108 storageEl.save(storageName);
43112 // In IE7, keys cannot start with a digit or contain certain chars.
43113 // See https://github.com/marcuswestin/store.js/issues/40
43114 // See https://github.com/marcuswestin/store.js/issues/83
43117 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
43119 function fixKey(key) {
43120 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___');
43123 function _makeIEStorageElFunction() {
43124 if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
43128 var scriptTag = 'script',
43131 storageEl; // Since #userData storage applies only to specific paths, we need to
43132 // somehow link our data to a specific path. We choose /favicon.ico
43133 // as a pretty safe option, since all browsers already make a request to
43134 // this URL anyway and being a 404 will not hurt us here. We wrap an
43135 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
43136 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
43137 // since the iframe access rules appear to allow direct access and
43138 // manipulation of the document element, even for a 404 page. This
43139 // document can be used instead of the current document (which would
43140 // have been limited to the current path) to perform #userData storage.
43143 /* global ActiveXObject */
43144 storageContainer = new ActiveXObject('htmlfile');
43145 storageContainer.open();
43146 storageContainer.write('<' + scriptTag + '>document.w=window</' + scriptTag + '><iframe src="/favicon.ico"></iframe>');
43147 storageContainer.close();
43148 storageOwner = storageContainer.w.frames[0].document;
43149 storageEl = storageOwner.createElement('div');
43151 // somehow ActiveXObject instantiation failed (perhaps some special
43152 // security settings or otherwse), fall back to per-path storage
43153 storageEl = doc.createElement('div');
43154 storageOwner = doc.body;
43157 return function (storeFunction) {
43158 var args = [].slice.call(arguments, 0);
43159 args.unshift(storageEl); // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
43160 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
43162 storageOwner.appendChild(storageEl);
43163 storageEl.addBehavior('#default#userData');
43164 storageEl.load(storageName);
43165 storeFunction.apply(this, args);
43166 storageOwner.removeChild(storageEl);
43171 // doesn't work but cookies do. This implementation is adopted from
43172 // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
43174 var Global$4 = util.Global;
43175 var trim$4 = util.trim;
43176 var cookieStorage = {
43177 name: 'cookieStorage',
43182 clearAll: clearAll$3
43184 var doc$1 = Global$4.document;
43186 function read$3(key) {
43187 if (!key || !_has(key)) {
43191 var regexpStr = "(?:^|.*;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
43192 return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"));
43195 function each$5(callback) {
43196 var cookies = doc$1.cookie.split(/; ?/g);
43198 for (var i = cookies.length - 1; i >= 0; i--) {
43199 if (!trim$4(cookies[i])) {
43203 var kvp = cookies[i].split('=');
43204 var key = unescape(kvp[0]);
43205 var val = unescape(kvp[1]);
43206 callback(val, key);
43210 function write$3(key, data) {
43215 doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
43218 function remove$5(key) {
43219 if (!key || !_has(key)) {
43223 doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
43226 function clearAll$3() {
43227 each$5(function (_, key) {
43232 function _has(key) {
43233 return new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=").test(doc$1.cookie);
43236 var Global$5 = util.Global;
43237 var sessionStorage_1 = {
43238 name: 'sessionStorage',
43243 clearAll: clearAll$4
43246 function sessionStorage() {
43247 return Global$5.sessionStorage;
43250 function read$4(key) {
43251 return sessionStorage().getItem(key);
43254 function write$4(key, data) {
43255 return sessionStorage().setItem(key, data);
43258 function each$6(fn) {
43259 for (var i = sessionStorage().length - 1; i >= 0; i--) {
43260 var key = sessionStorage().key(i);
43261 fn(read$4(key), key);
43265 function remove$6(key) {
43266 return sessionStorage().removeItem(key);
43269 function clearAll$4() {
43270 return sessionStorage().clear();
43273 // memoryStorage is a useful last fallback to ensure that the store
43274 // is functions (meaning store.get(), store.set(), etc will all function).
43275 // However, stored values will not persist when the browser navigates to
43276 // a new page or reloads the current page.
43277 var memoryStorage_1 = {
43278 name: 'memoryStorage',
43283 clearAll: clearAll$5
43285 var memoryStorage = {};
43287 function read$5(key) {
43288 return memoryStorage[key];
43291 function write$5(key, data) {
43292 memoryStorage[key] = data;
43295 function each$7(callback) {
43296 for (var key in memoryStorage) {
43297 if (memoryStorage.hasOwnProperty(key)) {
43298 callback(memoryStorage[key], key);
43303 function remove$7(key) {
43304 delete memoryStorage[key];
43307 function clearAll$5(key) {
43308 memoryStorage = {};
43311 var all = [// Listed in order of usage preference
43312 localStorage_1, oldFFGlobalStorage, oldIEUserDataStorage, cookieStorage, sessionStorage_1, memoryStorage_1];
43314 /* eslint-disable */
43318 // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
43319 // See http://www.JSON.org/js.html
43320 // This code should be minified before deployment.
43321 // See http://javascript.crockford.com/jsmin.html
43322 // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
43324 // This file creates a global JSON object containing two methods: stringify
43325 // and parse. This file provides the ES5 JSON capability to ES3 systems.
43326 // If a project might run on IE8 or earlier, then this file should be included.
43327 // This file does nothing on ES5 systems.
43328 // JSON.stringify(value, replacer, space)
43329 // value any JavaScript value, usually an object or array.
43330 // replacer an optional parameter that determines how object
43331 // values are stringified for objects. It can be a
43332 // function or an array of strings.
43333 // space an optional parameter that specifies the indentation
43334 // of nested structures. If it is omitted, the text will
43335 // be packed without extra whitespace. If it is a number,
43336 // it will specify the number of spaces to indent at each
43337 // level. If it is a string (such as "\t" or " "),
43338 // it contains the characters used to indent at each level.
43339 // This method produces a JSON text from a JavaScript value.
43340 // When an object value is found, if the object contains a toJSON
43341 // method, its toJSON method will be called and the result will be
43342 // stringified. A toJSON method does not serialize: it returns the
43343 // value represented by the name/value pair that should be serialized,
43344 // or undefined if nothing should be serialized. The toJSON method
43345 // will be passed the key associated with the value, and this will be
43346 // bound to the value.
43347 // For example, this would serialize Dates as ISO strings.
43348 // Date.prototype.toJSON = function (key) {
43350 // // Format integers to have at least two digits.
43355 // return this.getUTCFullYear() + "-" +
43356 // f(this.getUTCMonth() + 1) + "-" +
43357 // f(this.getUTCDate()) + "T" +
43358 // f(this.getUTCHours()) + ":" +
43359 // f(this.getUTCMinutes()) + ":" +
43360 // f(this.getUTCSeconds()) + "Z";
43362 // You can provide an optional replacer method. It will be passed the
43363 // key and value of each member, with this bound to the containing
43364 // object. The value that is returned from your method will be
43365 // serialized. If your method returns undefined, then the member will
43366 // be excluded from the serialization.
43367 // If the replacer parameter is an array of strings, then it will be
43368 // used to select the members to be serialized. It filters the results
43369 // such that only members with keys listed in the replacer array are
43371 // Values that do not have JSON representations, such as undefined or
43372 // functions, will not be serialized. Such values in objects will be
43373 // dropped; in arrays they will be replaced with null. You can use
43374 // a replacer function to replace those with JSON values.
43375 // JSON.stringify(undefined) returns undefined.
43376 // The optional space parameter produces a stringification of the
43377 // value that is filled with line breaks and indentation to make it
43379 // If the space parameter is a non-empty string, then that string will
43380 // be used for indentation. If the space parameter is a number, then
43381 // the indentation will be that many spaces.
43383 // text = JSON.stringify(["e", {pluribus: "unum"}]);
43384 // // text is '["e",{"pluribus":"unum"}]'
43385 // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
43386 // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
43387 // text = JSON.stringify([new Date()], function (key, value) {
43388 // return this[key] instanceof Date
43389 // ? "Date(" + this[key] + ")"
43392 // // text is '["Date(---current time---)"]'
43393 // JSON.parse(text, reviver)
43394 // This method parses a JSON text to produce an object or array.
43395 // It can throw a SyntaxError exception.
43396 // The optional reviver parameter is a function that can filter and
43397 // transform the results. It receives each of the keys and values,
43398 // and its return value is used instead of the original value.
43399 // If it returns what it received, then the structure is not modified.
43400 // If it returns undefined then the member is deleted.
43402 // // Parse the text. Values that look like ISO date strings will
43403 // // be converted to Date objects.
43404 // myData = JSON.parse(text, function (key, value) {
43406 // if (typeof value === "string") {
43408 // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
43410 // return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
43416 // myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
43418 // if (typeof value === "string" &&
43419 // value.slice(0, 5) === "Date(" &&
43420 // value.slice(-1) === ")") {
43421 // d = new Date(value.slice(5, -1));
43428 // This is a reference implementation. You are free to copy, modify, or
43436 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
43437 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
43438 lastIndex, length, parse, prototype, push, replace, slice, stringify,
43439 test, toJSON, toString, valueOf
43441 // Create a JSON object only if one does not already exist. We create the
43442 // methods in a closure to avoid creating global variables.
43443 if ((typeof JSON === "undefined" ? "undefined" : _typeof(JSON)) !== "object") {
43449 var rx_one = /^[\],:{}\s]*$/;
43450 var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
43451 var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
43452 var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
43453 var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
43454 var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
43457 // Format integers to have at least two digits.
43458 return n < 10 ? "0" + n : n;
43461 function this_value() {
43462 return this.valueOf();
43465 if (typeof Date.prototype.toJSON !== "function") {
43466 Date.prototype.toJSON = function () {
43467 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;
43470 Boolean.prototype.toJSON = this_value;
43471 Number.prototype.toJSON = this_value;
43472 String.prototype.toJSON = this_value;
43480 function quote(string) {
43481 // If the string contains no control characters, no quote characters, and no
43482 // backslash characters, then we can safely slap some quotes around it.
43483 // Otherwise we must also replace the offending characters with safe escape
43485 rx_escapable.lastIndex = 0;
43486 return rx_escapable.test(string) ? "\"" + string.replace(rx_escapable, function (a) {
43488 return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
43489 }) + "\"" : "\"" + string + "\"";
43492 function str(key, holder) {
43493 // Produce a string from holder[key].
43494 var i; // The loop counter.
43496 var k; // The member key.
43498 var v; // The member value.
43503 var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value.
43505 if (value && _typeof(value) === "object" && typeof value.toJSON === "function") {
43506 value = value.toJSON(key);
43507 } // If we were called with a replacer function, then call the replacer to
43508 // obtain a replacement value.
43511 if (typeof rep === "function") {
43512 value = rep.call(holder, key, value);
43513 } // What happens next depends on the value's type.
43516 switch (_typeof(value)) {
43518 return quote(value);
43521 // JSON numbers must be finite. Encode non-finite numbers as null.
43522 return isFinite(value) ? String(value) : "null";
43526 // If the value is a boolean or null, convert it to a string. Note:
43527 // typeof null does not produce "null". The case is included here in
43528 // the remote chance that this gets fixed someday.
43529 return String(value);
43530 // If the type is "object", we might be dealing with an object or an array or
43534 // Due to a specification blunder in ECMAScript, typeof null is "object",
43535 // so watch out for that case.
43538 } // Make an array to hold the partial results of stringifying this object value.
43542 partial = []; // Is the value an array?
43544 if (Object.prototype.toString.apply(value) === "[object Array]") {
43545 // The value is an array. Stringify every element. Use null as a placeholder
43546 // for non-JSON values.
43547 length = value.length;
43549 for (i = 0; i < length; i += 1) {
43550 partial[i] = str(i, value) || "null";
43551 } // Join all of the elements together, separated with commas, and wrap them in
43555 v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
43558 } // If the replacer is an array, use it to select the members to be stringified.
43561 if (rep && _typeof(rep) === "object") {
43562 length = rep.length;
43564 for (i = 0; i < length; i += 1) {
43565 if (typeof rep[i] === "string") {
43570 partial.push(quote(k) + (gap ? ": " : ":") + v);
43575 // Otherwise, iterate through all of the keys in the object.
43577 if (Object.prototype.hasOwnProperty.call(value, k)) {
43581 partial.push(quote(k) + (gap ? ": " : ":") + v);
43585 } // Join all of the member texts together, separated with commas,
43586 // and wrap them in braces.
43589 v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
43593 } // If the JSON object does not yet have a stringify method, give it one.
43596 if (typeof JSON.stringify !== "function") {
43598 // table of character substitutions
43608 JSON.stringify = function (value, replacer, space) {
43609 // The stringify method takes a value and an optional replacer, and an optional
43610 // space parameter, and returns a JSON text. The replacer can be a function
43611 // that can replace values, or an array of strings that will select the keys.
43612 // A default replacer method can be provided. Use of the space parameter can
43613 // produce text that is more easily readable.
43616 indent = ""; // If the space parameter is a number, make an indent string containing that
43619 if (typeof space === "number") {
43620 for (i = 0; i < space; i += 1) {
43622 } // If the space parameter is a string, it will be used as the indent string.
43624 } else if (typeof space === "string") {
43626 } // If there is a replacer, it must be a function or an array.
43627 // Otherwise, throw an error.
43632 if (replacer && typeof replacer !== "function" && (_typeof(replacer) !== "object" || typeof replacer.length !== "number")) {
43633 throw new Error("JSON.stringify");
43634 } // Make a fake root object containing our value under the key of "".
43635 // Return the result of stringifying the value.
43642 } // If the JSON object does not yet have a parse method, give it one.
43645 if (typeof JSON.parse !== "function") {
43646 JSON.parse = function (text, reviver) {
43647 // The parse method takes a text and an optional reviver function, and returns
43648 // a JavaScript value if the text is a valid JSON text.
43651 function walk(holder, key) {
43652 // The walk method is used to recursively walk the resulting structure so
43653 // that modifications can be made.
43656 var value = holder[key];
43658 if (value && _typeof(value) === "object") {
43660 if (Object.prototype.hasOwnProperty.call(value, k)) {
43661 v = walk(value, k);
43663 if (v !== undefined) {
43672 return reviver.call(holder, key, value);
43673 } // Parsing happens in four stages. In the first stage, we replace certain
43674 // Unicode characters with escape sequences. JavaScript handles many characters
43675 // incorrectly, either silently deleting them, or treating them as line endings.
43678 text = String(text);
43679 rx_dangerous.lastIndex = 0;
43681 if (rx_dangerous.test(text)) {
43682 text = text.replace(rx_dangerous, function (a) {
43683 return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
43685 } // In the second stage, we run the text against regular expressions that look
43686 // for non-JSON patterns. We are especially concerned with "()" and "new"
43687 // because they can cause invocation, and "=" because it can cause mutation.
43688 // But just to be safe, we want to reject all unexpected forms.
43689 // We split the second stage into 4 regexp operations in order to work around
43690 // crippling inefficiencies in IE's and Safari's regexp engines. First we
43691 // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
43692 // replace all simple value tokens with "]" characters. Third, we delete all
43693 // open brackets that follow a colon or comma or that begin the text. Finally,
43694 // we look to see that the remaining characters are only whitespace or "]" or
43695 // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
43698 if (rx_one.test(text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, ""))) {
43699 // In the third stage we use the eval function to compile the text into a
43700 // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
43701 // in JavaScript: it can begin a block or an object literal. We wrap the text
43702 // in parens to eliminate the ambiguity.
43703 j = eval("(" + text + ")"); // In the optional fourth stage, we recursively walk the new structure, passing
43704 // each name/value pair to a reviver function for possible transformation.
43706 return typeof reviver === "function" ? walk({
43709 } // If the text is not JSON parseable, then a SyntaxError is thrown.
43712 throw new SyntaxError("JSON.parse");
43717 var json2 = json2Plugin;
43719 function json2Plugin() {
43723 var plugins = [json2];
43724 var store_legacy = storeEngine.createStore(all, plugins);
43727 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
43728 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
43729 // does not support custom headers, which this uses everywhere.
43732 var osmAuth = function osmAuth(o) {
43733 var oauth = {}; // authenticated users will also have a request token secret, but it's
43734 // not used in transactions with the server
43736 oauth.authenticated = function () {
43737 return !!(token('oauth_token') && token('oauth_token_secret'));
43740 oauth.logout = function () {
43741 token('oauth_token', '');
43742 token('oauth_token_secret', '');
43743 token('oauth_request_token_secret', '');
43745 }; // TODO: detect lack of click event
43748 oauth.authenticate = function (callback) {
43749 if (oauth.authenticated()) return callback();
43750 oauth.logout(); // ## Getting a request token
43752 var params = timenonce(getAuth(o)),
43753 url = o.url + '/oauth/request_token';
43754 params.oauth_signature = ohauth_1.signature(o.oauth_secret, '', ohauth_1.baseString('POST', url, params));
43756 if (!o.singlepage) {
43757 // Create a 600x550 popup window in the center of the screen
43760 settings = [['width', w], ['height', h], ['left', screen.width / 2 - w / 2], ['top', screen.height / 2 - h / 2]].map(function (x) {
43761 return x.join('=');
43763 popup = window.open('about:blank', 'oauth_window', settings);
43764 oauth.popupWindow = popup;
43767 var error = new Error('Popup was blocked');
43768 error.status = 'popup-blocked';
43771 } // Request a request token. When this is complete, the popup
43772 // window is redirected to OSM's authorization page.
43775 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
43778 function reqTokenDone(err, xhr) {
43780 if (err) return callback(err);
43781 var resp = ohauth_1.stringQs(xhr.response);
43782 token('oauth_request_token_secret', resp.oauth_token_secret);
43783 var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
43784 oauth_token: resp.oauth_token,
43785 oauth_callback: resolveUrl$1(o.landing)
43788 if (o.singlepage) {
43789 location.href = authorize_url;
43791 popup.location = authorize_url;
43793 } // Called by a function in a landing page, in the popup window. The
43794 // window closes itself.
43797 window.authComplete = function (token) {
43798 var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
43799 get_access_token(oauth_token.oauth_token);
43800 delete window.authComplete;
43801 }; // ## Getting an request token
43803 // At this point we have an `oauth_token`, brought in from a function
43804 // call on a landing page popup.
43807 function get_access_token(oauth_token) {
43808 var url = o.url + '/oauth/access_token',
43809 params = timenonce(getAuth(o)),
43810 request_token_secret = token('oauth_request_token_secret');
43811 params.oauth_token = oauth_token;
43812 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
43814 // The final token required for authentication. At this point
43815 // we have a `request token secret`
43817 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
43821 function accessTokenDone(err, xhr) {
43823 if (err) return callback(err);
43824 var access_token = ohauth_1.stringQs(xhr.response);
43825 token('oauth_token', access_token.oauth_token);
43826 token('oauth_token_secret', access_token.oauth_token_secret);
43827 callback(null, oauth);
43831 oauth.bringPopupWindowToFront = function () {
43832 var brougtPopupToFront = false;
43835 // This may cause a cross-origin error:
43836 // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
43837 if (oauth.popupWindow && !oauth.popupWindow.closed) {
43838 oauth.popupWindow.focus();
43839 brougtPopupToFront = true;
43841 } catch (err) {// Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
43844 return brougtPopupToFront;
43847 oauth.bootstrapToken = function (oauth_token, callback) {
43848 // ## Getting an request token
43849 // At this point we have an `oauth_token`, brought in from a function
43850 // call on a landing page popup.
43851 function get_access_token(oauth_token) {
43852 var url = o.url + '/oauth/access_token',
43853 params = timenonce(getAuth(o)),
43854 request_token_secret = token('oauth_request_token_secret');
43855 params.oauth_token = oauth_token;
43856 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
43857 // The final token required for authentication. At this point
43858 // we have a `request token secret`
43860 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
43864 function accessTokenDone(err, xhr) {
43866 if (err) return callback(err);
43867 var access_token = ohauth_1.stringQs(xhr.response);
43868 token('oauth_token', access_token.oauth_token);
43869 token('oauth_token_secret', access_token.oauth_token_secret);
43870 callback(null, oauth);
43873 get_access_token(oauth_token);
43876 // A single XMLHttpRequest wrapper that does authenticated calls if the
43877 // user has logged in.
43880 oauth.xhr = function (options, callback) {
43881 if (!oauth.authenticated()) {
43883 return oauth.authenticate(run);
43885 callback('not authenticated', null);
43893 var params = timenonce(getAuth(o)),
43894 oauth_token_secret = token('oauth_token_secret'),
43895 url = options.prefix !== false ? o.url + options.path : options.path,
43896 url_parts = url.replace(/#.*$/, '').split('?', 2),
43897 base_url = url_parts[0],
43898 query = url_parts.length === 2 ? url_parts[1] : ''; // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
43900 if ((!options.options || !options.options.header || options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') && options.content) {
43901 params = immutable(params, ohauth_1.stringQs(options.content));
43904 params.oauth_token = token('oauth_token');
43905 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))));
43906 return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
43909 function done(err, xhr) {
43910 if (err) return callback(err);else if (xhr.responseXML) return callback(err, xhr.responseXML);else return callback(err, xhr.response);
43912 }; // pre-authorize this object, if we can just get a token and token_secret
43916 oauth.preauth = function (c) {
43918 if (c.oauth_token) token('oauth_token', c.oauth_token);
43919 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
43923 oauth.options = function (_) {
43924 if (!arguments.length) return o;
43926 o.url = o.url || 'https://www.openstreetmap.org';
43927 o.landing = o.landing || 'land.html';
43928 o.singlepage = o.singlepage || false; // Optional loading and loading-done functions for nice UI feedback.
43929 // by default, no-ops
43931 o.loading = o.loading || function () {};
43933 o.done = o.done || function () {};
43935 return oauth.preauth(o);
43936 }; // 'stamp' an authentication object from `getAuth()`
43937 // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
43941 function timenonce(o) {
43942 o.oauth_timestamp = ohauth_1.timestamp();
43943 o.oauth_nonce = ohauth_1.nonce();
43945 } // get/set tokens. These are prefixed with the base URL so that `osm-auth`
43946 // can be used with multiple APIs and the keys in `localStorage`
43952 if (store_legacy.enabled) {
43953 token = function token(x, y) {
43954 if (arguments.length === 1) return store_legacy.get(o.url + x);else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
43959 token = function token(x, y) {
43960 if (arguments.length === 1) return storage[o.url + x];else if (arguments.length === 2) return storage[o.url + x] = y;
43962 } // Get an authentication object. If you just add and remove properties
43963 // from a single object, you'll need to use `delete` to make sure that
43964 // it doesn't contain undesired properties for authentication
43967 function getAuth(o) {
43969 oauth_consumer_key: o.oauth_consumer_key,
43970 oauth_signature_method: 'HMAC-SHA1'
43972 } // potentially pre-authorize
43979 var JXON = new function () {
43980 var sValueProp = 'keyValue',
43981 sAttributesProp = 'keyAttributes',
43984 /* you can customize these values */
43987 rIsBool = /^(?:true|false)$/i;
43989 function parseText(sValue) {
43990 if (rIsNull.test(sValue)) {
43994 if (rIsBool.test(sValue)) {
43995 return sValue.toLowerCase() === 'true';
43998 if (isFinite(sValue)) {
43999 return parseFloat(sValue);
44002 if (isFinite(Date.parse(sValue))) {
44003 return new Date(sValue);
44009 function EmptyTree() {}
44011 EmptyTree.prototype.toString = function () {
44015 EmptyTree.prototype.valueOf = function () {
44019 function objectify(vValue) {
44020 return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
44023 function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
44024 var nLevelStart = aCache.length,
44025 bChildren = oParentNode.hasChildNodes(),
44026 bAttributes = oParentNode.hasAttributes(),
44027 bHighVerb = Boolean(nVerb & 2);
44031 sCollectedTxt = '',
44032 vResult = bHighVerb ? {} :
44033 /* put here the default value for empty nodes: */
44037 for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
44038 oNode = oParentNode.childNodes.item(nItem);
44040 if (oNode.nodeType === 4) {
44041 sCollectedTxt += oNode.nodeValue;
44043 /* nodeType is 'CDATASection' (4) */
44044 else if (oNode.nodeType === 3) {
44045 sCollectedTxt += oNode.nodeValue.trim();
44047 /* nodeType is 'Text' (3) */
44048 else if (oNode.nodeType === 1 && !oNode.prefix) {
44049 aCache.push(oNode);
44051 /* nodeType is 'Element' (1) */
44056 var nLevelEnd = aCache.length,
44057 vBuiltVal = parseText(sCollectedTxt);
44059 if (!bHighVerb && (bChildren || bAttributes)) {
44060 vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
44063 for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
44064 sProp = aCache[nElId].nodeName.toLowerCase();
44065 vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
44067 if (vResult.hasOwnProperty(sProp)) {
44068 if (vResult[sProp].constructor !== Array) {
44069 vResult[sProp] = [vResult[sProp]];
44072 vResult[sProp].push(vContent);
44074 vResult[sProp] = vContent;
44080 var nAttrLen = oParentNode.attributes.length,
44081 sAPrefix = bNesteAttr ? '' : sAttrPref,
44082 oAttrParent = bNesteAttr ? {} : vResult;
44084 for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
44085 oAttrib = oParentNode.attributes.item(nAttrib);
44086 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
44091 Object.freeze(oAttrParent);
44094 vResult[sAttributesProp] = oAttrParent;
44095 nLength -= nAttrLen - 1;
44099 if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
44100 vResult[sValueProp] = vBuiltVal;
44101 } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
44102 vResult = vBuiltVal;
44105 if (bFreeze && (bHighVerb || nLength > 0)) {
44106 Object.freeze(vResult);
44109 aCache.length = nLevelStart;
44113 function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
44114 var vValue, oChild;
44116 if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
44117 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString()));
44118 /* verbosity level is 0 */
44119 } else if (oParentObj.constructor === Date) {
44120 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
44123 for (var sName in oParentObj) {
44124 vValue = oParentObj[sName];
44126 if (isFinite(sName) || vValue instanceof Function) {
44129 /* verbosity level is 0 */
44132 if (sName === sValueProp) {
44133 if (vValue !== null && vValue !== true) {
44134 oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
44136 } else if (sName === sAttributesProp) {
44137 /* verbosity level is 3 */
44138 for (var sAttrib in vValue) {
44139 oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
44141 } else if (sName.charAt(0) === sAttrPref) {
44142 oParentEl.setAttribute(sName.slice(1), vValue);
44143 } else if (vValue.constructor === Array) {
44144 for (var nItem = 0; nItem < vValue.length; nItem++) {
44145 oChild = oXMLDoc.createElement(sName);
44146 loadObjTree(oXMLDoc, oChild, vValue[nItem]);
44147 oParentEl.appendChild(oChild);
44150 oChild = oXMLDoc.createElement(sName);
44152 if (vValue instanceof Object) {
44153 loadObjTree(oXMLDoc, oChild, vValue);
44154 } else if (vValue !== null && vValue !== true) {
44155 oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
44158 oParentEl.appendChild(oChild);
44163 this.build = function (oXMLParent, nVerbosity
44170 var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 :
44171 /* put here the default verbosity level: */
44174 return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
44177 this.unbuild = function (oObjTree) {
44178 var oNewDoc = document.implementation.createDocument('', '', null);
44179 loadObjTree(oNewDoc, oNewDoc, oObjTree);
44183 this.stringify = function (oObjTree) {
44184 return new XMLSerializer().serializeToString(JXON.unbuild(oObjTree));
44186 }(); // var myObject = JXON.build(doc);
44187 // we got our javascript object! try: alert(JSON.stringify(myObject));
44188 // var newDoc = JXON.unbuild(myObject);
44189 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
44191 var tiler$5 = utilTiler();
44192 var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
44193 var urlroot = 'https://www.openstreetmap.org';
44194 var oauth = osmAuth({
44196 oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
44197 oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
44198 loading: authLoading,
44200 }); // hardcode default block of Google Maps
44202 var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
44224 var _cachedApiStatus;
44226 var _changeset = {};
44228 var _deferred = new Set();
44230 var _connectionID = 1;
44231 var _tileZoom$3 = 16;
44232 var _noteZoom = 12;
44234 var _rateLimitError;
44236 var _userChangesets;
44240 var _off; // set a default but also load this from the API status
44243 var _maxWayNodes = 2000;
44245 function authLoading() {
44246 dispatch$6.call('authLoading');
44249 function authDone() {
44250 dispatch$6.call('authDone');
44253 function abortRequest$5(controllerOrXHR) {
44254 if (controllerOrXHR) {
44255 controllerOrXHR.abort();
44259 function hasInflightRequests(cache) {
44260 return Object.keys(cache.inflight).length;
44263 function abortUnwantedRequests$3(cache, visibleTiles) {
44264 Object.keys(cache.inflight).forEach(function (k) {
44265 if (cache.toLoad[k]) return;
44266 if (visibleTiles.find(function (tile) {
44267 return k === tile.id;
44269 abortRequest$5(cache.inflight[k]);
44270 delete cache.inflight[k];
44274 function getLoc(attrs) {
44275 var lon = attrs.lon && attrs.lon.value;
44276 var lat = attrs.lat && attrs.lat.value;
44277 return [parseFloat(lon), parseFloat(lat)];
44280 function getNodes(obj) {
44281 var elems = obj.getElementsByTagName('nd');
44282 var nodes = new Array(elems.length);
44284 for (var i = 0, l = elems.length; i < l; i++) {
44285 nodes[i] = 'n' + elems[i].attributes.ref.value;
44291 function getNodesJSON(obj) {
44292 var elems = obj.nodes;
44293 var nodes = new Array(elems.length);
44295 for (var i = 0, l = elems.length; i < l; i++) {
44296 nodes[i] = 'n' + elems[i];
44302 function getTags(obj) {
44303 var elems = obj.getElementsByTagName('tag');
44306 for (var i = 0, l = elems.length; i < l; i++) {
44307 var attrs = elems[i].attributes;
44308 tags[attrs.k.value] = attrs.v.value;
44314 function getMembers(obj) {
44315 var elems = obj.getElementsByTagName('member');
44316 var members = new Array(elems.length);
44318 for (var i = 0, l = elems.length; i < l; i++) {
44319 var attrs = elems[i].attributes;
44321 id: attrs.type.value[0] + attrs.ref.value,
44322 type: attrs.type.value,
44323 role: attrs.role.value
44330 function getMembersJSON(obj) {
44331 var elems = obj.members;
44332 var members = new Array(elems.length);
44334 for (var i = 0, l = elems.length; i < l; i++) {
44335 var attrs = elems[i];
44337 id: attrs.type[0] + attrs.ref,
44346 function getVisible(attrs) {
44347 return !attrs.visible || attrs.visible.value !== 'false';
44350 function parseComments(comments) {
44351 var parsedComments = []; // for each comment
44353 for (var i = 0; i < comments.length; i++) {
44354 var comment = comments[i];
44356 if (comment.nodeName === 'comment') {
44357 var childNodes = comment.childNodes;
44358 var parsedComment = {};
44360 for (var j = 0; j < childNodes.length; j++) {
44361 var node = childNodes[j];
44362 var nodeName = node.nodeName;
44363 if (nodeName === '#text') continue;
44364 parsedComment[nodeName] = node.textContent;
44366 if (nodeName === 'uid') {
44367 var uid = node.textContent;
44369 if (uid && !_userCache.user[uid]) {
44370 _userCache.toLoad[uid] = true;
44375 if (parsedComment) {
44376 parsedComments.push(parsedComment);
44381 return parsedComments;
44384 function encodeNoteRtree(note) {
44394 var jsonparsers = {
44395 node: function nodeData(obj, uid) {
44396 return new osmNode({
44398 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44399 version: obj.version && obj.version.toString(),
44400 changeset: obj.changeset && obj.changeset.toString(),
44401 timestamp: obj.timestamp,
44403 uid: obj.uid && obj.uid.toString(),
44404 loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
44408 way: function wayData(obj, uid) {
44409 return new osmWay({
44411 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44412 version: obj.version && obj.version.toString(),
44413 changeset: obj.changeset && obj.changeset.toString(),
44414 timestamp: obj.timestamp,
44416 uid: obj.uid && obj.uid.toString(),
44418 nodes: getNodesJSON(obj)
44421 relation: function relationData(obj, uid) {
44422 return new osmRelation({
44424 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44425 version: obj.version && obj.version.toString(),
44426 changeset: obj.changeset && obj.changeset.toString(),
44427 timestamp: obj.timestamp,
44429 uid: obj.uid && obj.uid.toString(),
44431 members: getMembersJSON(obj)
44436 function parseJSON(payload, callback, options) {
44437 options = Object.assign({
44443 message: 'No JSON',
44448 var json = payload;
44449 if (_typeof(json) !== 'object') json = JSON.parse(payload);
44450 if (!json.elements) return callback({
44451 message: 'No JSON',
44454 var children = json.elements;
44455 var handle = window.requestIdleCallback(function () {
44459 for (var i = 0; i < children.length; i++) {
44460 result = parseChild(children[i]);
44461 if (result) results.push(result);
44464 callback(null, results);
44467 _deferred.add(handle);
44469 function parseChild(child) {
44470 var parser = jsonparsers[child.type];
44471 if (!parser) return null;
44473 uid = osmEntity.id.fromOSM(child.type, child.id);
44475 if (options.skipSeen) {
44476 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
44478 _tileCache.seen[uid] = true;
44481 return parser(child, uid);
44486 node: function nodeData(obj, uid) {
44487 var attrs = obj.attributes;
44488 return new osmNode({
44490 visible: getVisible(attrs),
44491 version: attrs.version.value,
44492 changeset: attrs.changeset && attrs.changeset.value,
44493 timestamp: attrs.timestamp && attrs.timestamp.value,
44494 user: attrs.user && attrs.user.value,
44495 uid: attrs.uid && attrs.uid.value,
44496 loc: getLoc(attrs),
44500 way: function wayData(obj, uid) {
44501 var attrs = obj.attributes;
44502 return new osmWay({
44504 visible: getVisible(attrs),
44505 version: attrs.version.value,
44506 changeset: attrs.changeset && attrs.changeset.value,
44507 timestamp: attrs.timestamp && attrs.timestamp.value,
44508 user: attrs.user && attrs.user.value,
44509 uid: attrs.uid && attrs.uid.value,
44510 tags: getTags(obj),
44511 nodes: getNodes(obj)
44514 relation: function relationData(obj, uid) {
44515 var attrs = obj.attributes;
44516 return new osmRelation({
44518 visible: getVisible(attrs),
44519 version: attrs.version.value,
44520 changeset: attrs.changeset && attrs.changeset.value,
44521 timestamp: attrs.timestamp && attrs.timestamp.value,
44522 user: attrs.user && attrs.user.value,
44523 uid: attrs.uid && attrs.uid.value,
44524 tags: getTags(obj),
44525 members: getMembers(obj)
44528 note: function parseNote(obj, uid) {
44529 var attrs = obj.attributes;
44530 var childNodes = obj.childNodes;
44533 props.loc = getLoc(attrs); // if notes are coincident, move them apart slightly
44535 var coincident = false;
44536 var epsilon = 0.00001;
44540 props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
44543 var bbox = geoExtent(props.loc).bbox();
44544 coincident = _noteCache.rtree.search(bbox).length;
44545 } while (coincident); // parse note contents
44548 for (var i = 0; i < childNodes.length; i++) {
44549 var node = childNodes[i];
44550 var nodeName = node.nodeName;
44551 if (nodeName === '#text') continue; // if the element is comments, parse the comments
44553 if (nodeName === 'comments') {
44554 props[nodeName] = parseComments(node.childNodes);
44556 props[nodeName] = node.textContent;
44560 var note = new osmNote(props);
44561 var item = encodeNoteRtree(note);
44562 _noteCache.note[note.id] = note;
44564 _noteCache.rtree.insert(item);
44568 user: function parseUser(obj, uid) {
44569 var attrs = obj.attributes;
44572 display_name: attrs.display_name && attrs.display_name.value,
44573 account_created: attrs.account_created && attrs.account_created.value,
44574 changesets_count: '0',
44577 var img = obj.getElementsByTagName('img');
44579 if (img && img[0] && img[0].getAttribute('href')) {
44580 user.image_url = img[0].getAttribute('href');
44583 var changesets = obj.getElementsByTagName('changesets');
44585 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
44586 user.changesets_count = changesets[0].getAttribute('count');
44589 var blocks = obj.getElementsByTagName('blocks');
44591 if (blocks && blocks[0]) {
44592 var received = blocks[0].getElementsByTagName('received');
44594 if (received && received[0] && received[0].getAttribute('active')) {
44595 user.active_blocks = received[0].getAttribute('active');
44599 _userCache.user[uid] = user;
44600 delete _userCache.toLoad[uid];
44605 function parseXML(xml, callback, options) {
44606 options = Object.assign({
44610 if (!xml || !xml.childNodes) {
44617 var root = xml.childNodes[0];
44618 var children = root.childNodes;
44619 var handle = window.requestIdleCallback(function () {
44623 for (var i = 0; i < children.length; i++) {
44624 result = parseChild(children[i]);
44625 if (result) results.push(result);
44628 callback(null, results);
44631 _deferred.add(handle);
44633 function parseChild(child) {
44634 var parser = parsers[child.nodeName];
44635 if (!parser) return null;
44638 if (child.nodeName === 'user') {
44639 uid = child.attributes.id.value;
44641 if (options.skipSeen && _userCache.user[uid]) {
44642 delete _userCache.toLoad[uid];
44645 } else if (child.nodeName === 'note') {
44646 uid = child.getElementsByTagName('id')[0].textContent;
44648 uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
44650 if (options.skipSeen) {
44651 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
44653 _tileCache.seen[uid] = true;
44657 return parser(child, uid);
44659 } // replace or remove note from rtree
44662 function updateRtree$3(item, replace) {
44663 _noteCache.rtree.remove(item, function isEql(a, b) {
44664 return a.data.id === b.data.id;
44668 _noteCache.rtree.insert(item);
44672 function wrapcb(thisArg, callback, cid) {
44673 return function (err, result) {
44675 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
44676 if (err.status === 400 || err.status === 401 || err.status === 403) {
44680 return callback.call(thisArg, err);
44681 } else if (thisArg.getConnectionId() !== cid) {
44682 return callback.call(thisArg, {
44683 message: 'Connection Switched',
44687 return callback.call(thisArg, err, result);
44693 init: function init() {
44694 utilRebind(this, dispatch$6, 'on');
44696 reset: function reset() {
44697 Array.from(_deferred).forEach(function (handle) {
44698 window.cancelIdleCallback(handle);
44700 _deferred["delete"](handle);
44703 _userChangesets = undefined;
44704 _userDetails = undefined;
44705 _rateLimitError = undefined;
44706 Object.values(_tileCache.inflight).forEach(abortRequest$5);
44707 Object.values(_noteCache.inflight).forEach(abortRequest$5);
44708 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
44709 if (_changeset.inflight) abortRequest$5(_changeset.inflight);
44730 _cachedApiStatus = undefined;
44734 getConnectionId: function getConnectionId() {
44735 return _connectionID;
44737 changesetURL: function changesetURL(changesetID) {
44738 return urlroot + '/changeset/' + changesetID;
44740 changesetsURL: function changesetsURL(center, zoom) {
44741 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
44742 return urlroot + '/history#map=' + Math.floor(zoom) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
44744 entityURL: function entityURL(entity) {
44745 return urlroot + '/' + entity.type + '/' + entity.osmId();
44747 historyURL: function historyURL(entity) {
44748 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
44750 userURL: function userURL(username) {
44751 return urlroot + '/user/' + username;
44753 noteURL: function noteURL(note) {
44754 return urlroot + '/note/' + note.id;
44756 noteReportURL: function noteReportURL(note) {
44757 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
44759 // Generic method to load data from the OSM API
44760 // Can handle either auth or unauth calls.
44761 loadFromAPI: function loadFromAPI(path, callback, options) {
44762 options = Object.assign({
44766 var cid = _connectionID;
44768 function done(err, payload) {
44769 if (that.getConnectionId() !== cid) {
44770 if (callback) callback({
44771 message: 'Connection Switched',
44777 var isAuthenticated = that.authenticated(); // 400 Bad Request, 401 Unauthorized, 403 Forbidden
44778 // Logout and retry the request..
44780 if (isAuthenticated && err && err.status && (err.status === 400 || err.status === 401 || err.status === 403)) {
44782 that.loadFromAPI(path, callback, options); // else, no retry..
44784 // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
44785 // Set the rateLimitError flag and trigger a warning..
44786 if (!isAuthenticated && !_rateLimitError && err && err.status && (err.status === 509 || err.status === 429)) {
44787 _rateLimitError = err;
44788 dispatch$6.call('change');
44789 that.reloadApiStatus();
44790 } else if (err && _cachedApiStatus === 'online' || !err && _cachedApiStatus !== 'online') {
44791 // If the response's error state doesn't match the status,
44792 // it's likely we lost or gained the connection so reload the status
44793 that.reloadApiStatus();
44798 return callback(err);
44800 if (path.indexOf('.json') !== -1) {
44801 return parseJSON(payload, callback, options);
44803 return parseXML(payload, callback, options);
44810 if (this.authenticated()) {
44816 var url = urlroot + path;
44817 var controller = new AbortController();
44819 signal: controller.signal
44820 }).then(function (data) {
44822 })["catch"](function (err) {
44823 if (err.name === 'AbortError') return; // d3-fetch includes status in the error message,
44824 // but we can't access the response itself
44825 // https://github.com/d3/d3-fetch/issues/27
44827 var match = err.message.match(/^\d{3}/);
44832 statusText: err.message
44841 // Load a single entity by id (ways and relations use the `/full` call)
44842 // GET /api/0.6/node/#id
44843 // GET /api/0.6/[way|relation]/#id/full
44844 loadEntity: function loadEntity(id, callback) {
44845 var type = osmEntity.id.type(id);
44846 var osmID = osmEntity.id.toOSM(id);
44850 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json', function (err, entities) {
44851 if (callback) callback(err, {
44856 // Load a single entity with a specific version
44857 // GET /api/0.6/[node|way|relation]/#id/#version
44858 loadEntityVersion: function loadEntityVersion(id, version, callback) {
44859 var type = osmEntity.id.type(id);
44860 var osmID = osmEntity.id.toOSM(id);
44864 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/' + version + '.json', function (err, entities) {
44865 if (callback) callback(err, {
44870 // Load multiple entities in chunks
44871 // (note: callback may be called multiple times)
44872 // Unlike `loadEntity`, child nodes and members are not fetched
44873 // GET /api/0.6/[nodes|ways|relations]?#parameters
44874 loadMultiple: function loadMultiple(ids, callback) {
44876 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
44877 Object.keys(groups).forEach(function (k) {
44878 var type = k + 's'; // nodes, ways, relations
44880 var osmIDs = groups[k].map(function (id) {
44881 return osmEntity.id.toOSM(id);
44886 utilArrayChunk(osmIDs, 150).forEach(function (arr) {
44887 that.loadFromAPI('/api/0.6/' + type + '.json?' + type + '=' + arr.join(), function (err, entities) {
44888 if (callback) callback(err, {
44895 // Create, upload, and close a changeset
44896 // PUT /api/0.6/changeset/create
44897 // POST /api/0.6/changeset/#id/upload
44898 // PUT /api/0.6/changeset/#id/close
44899 putChangeset: function putChangeset(changeset, changes, callback) {
44900 var cid = _connectionID;
44902 if (_changeset.inflight) {
44904 message: 'Changeset already inflight',
44907 } else if (_changeset.open) {
44908 // reuse existing open changeset..
44909 return createdChangeset.call(this, null, _changeset.open);
44911 // Open a new changeset..
44914 path: '/api/0.6/changeset/create',
44917 'Content-Type': 'text/xml'
44920 content: JXON.stringify(changeset.asJXON())
44922 _changeset.inflight = oauth.xhr(options, wrapcb(this, createdChangeset, cid));
44925 function createdChangeset(err, changesetID) {
44926 _changeset.inflight = null;
44929 return callback(err, changeset);
44932 _changeset.open = changesetID;
44933 changeset = changeset.update({
44935 }); // Upload the changeset..
44939 path: '/api/0.6/changeset/' + changesetID + '/upload',
44942 'Content-Type': 'text/xml'
44945 content: JXON.stringify(changeset.osmChangeJXON(changes))
44947 _changeset.inflight = oauth.xhr(options, wrapcb(this, uploadedChangeset, cid));
44950 function uploadedChangeset(err) {
44951 _changeset.inflight = null;
44952 if (err) return callback(err, changeset); // Upload was successful, safe to call the callback.
44953 // Add delay to allow for postgres replication #1646 #2678
44955 window.setTimeout(function () {
44956 callback(null, changeset);
44958 _changeset.open = null; // At this point, we don't really care if the connection was switched..
44959 // Only try to close the changeset if we're still talking to the same server.
44961 if (this.getConnectionId() === cid) {
44962 // Still attempt to close changeset, but ignore response because #2667
44965 path: '/api/0.6/changeset/' + changeset.id + '/close',
44968 'Content-Type': 'text/xml'
44977 // Load multiple users in chunks
44978 // (note: callback may be called multiple times)
44979 // GET /api/0.6/users?users=#id1,#id2,...,#idn
44980 loadUsers: function loadUsers(uids, callback) {
44983 utilArrayUniq(uids).forEach(function (uid) {
44984 if (_userCache.user[uid]) {
44985 delete _userCache.toLoad[uid];
44986 cached.push(_userCache.user[uid]);
44992 if (cached.length || !this.authenticated()) {
44993 callback(undefined, cached);
44994 if (!this.authenticated()) return; // require auth
44997 utilArrayChunk(toLoad, 150).forEach(function (arr) {
45000 path: '/api/0.6/users?users=' + arr.join()
45001 }, wrapcb(this, done, _connectionID));
45004 function done(err, xml) {
45006 return callback(err);
45012 return parseXML(xml, function (err, results) {
45014 return callback(err);
45016 return callback(undefined, results);
45021 // Load a given user by id
45022 // GET /api/0.6/user/#id
45023 loadUser: function loadUser(uid, callback) {
45024 if (_userCache.user[uid] || !this.authenticated()) {
45026 delete _userCache.toLoad[uid];
45027 return callback(undefined, _userCache.user[uid]);
45032 path: '/api/0.6/user/' + uid
45033 }, wrapcb(this, done, _connectionID));
45035 function done(err, xml) {
45037 return callback(err);
45043 return parseXML(xml, function (err, results) {
45045 return callback(err);
45047 return callback(undefined, results[0]);
45052 // Load the details of the logged-in user
45053 // GET /api/0.6/user/details
45054 userDetails: function userDetails(callback) {
45055 if (_userDetails) {
45057 return callback(undefined, _userDetails);
45062 path: '/api/0.6/user/details'
45063 }, wrapcb(this, done, _connectionID));
45065 function done(err, xml) {
45067 return callback(err);
45073 return parseXML(xml, function (err, results) {
45075 return callback(err);
45077 _userDetails = results[0];
45078 return callback(undefined, _userDetails);
45083 // Load previous changesets for the logged in user
45084 // GET /api/0.6/changesets?user=#id
45085 userChangesets: function userChangesets(callback) {
45086 if (_userChangesets) {
45088 return callback(undefined, _userChangesets);
45091 this.userDetails(wrapcb(this, gotDetails, _connectionID));
45093 function gotDetails(err, user) {
45095 return callback(err);
45100 path: '/api/0.6/changesets?user=' + user.id
45101 }, wrapcb(this, done, _connectionID));
45104 function done(err, xml) {
45106 return callback(err);
45109 _userChangesets = Array.prototype.map.call(xml.getElementsByTagName('changeset'), function (changeset) {
45111 tags: getTags(changeset)
45113 }).filter(function (changeset) {
45114 var comment = changeset.tags.comment;
45115 return comment && comment !== '';
45117 return callback(undefined, _userChangesets);
45120 // Fetch the status of the OSM API
45121 // GET /api/capabilities
45122 status: function status(callback) {
45123 var url = urlroot + '/api/capabilities';
45124 var errback = wrapcb(this, done, _connectionID);
45125 d3_xml(url).then(function (data) {
45126 errback(null, data);
45127 })["catch"](function (err) {
45128 errback(err.message);
45131 function done(err, xml) {
45133 // the status is null if no response could be retrieved
45134 return callback(err, null);
45135 } // update blocklists
45138 var elements = xml.getElementsByTagName('blacklist');
45141 for (var i = 0; i < elements.length; i++) {
45142 var regexString = elements[i].getAttribute('regex'); // needs unencode?
45146 var regex = new RegExp(regexString);
45147 regexes.push(regex);
45154 if (regexes.length) {
45155 _imageryBlocklists = regexes;
45158 if (_rateLimitError) {
45159 return callback(_rateLimitError, 'rateLimited');
45161 var waynodes = xml.getElementsByTagName('waynodes');
45162 var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
45163 if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
45164 var apiStatus = xml.getElementsByTagName('status');
45165 var val = apiStatus[0].getAttribute('api');
45166 return callback(undefined, val);
45170 // Calls `status` and dispatches an `apiStatusChange` event if the returned
45171 // status differs from the cached status.
45172 reloadApiStatus: function reloadApiStatus() {
45173 // throttle to avoid unnecessary API calls
45174 if (!this.throttledReloadApiStatus) {
45176 this.throttledReloadApiStatus = throttle(function () {
45177 that.status(function (err, status) {
45178 if (status !== _cachedApiStatus) {
45179 _cachedApiStatus = status;
45180 dispatch$6.call('apiStatusChange', that, err, status);
45186 this.throttledReloadApiStatus();
45188 // Returns the maximum number of nodes a single way can have
45189 maxWayNodes: function maxWayNodes() {
45190 return _maxWayNodes;
45192 // Load data (entities) from the API in tiles
45193 // GET /api/0.6/map?bbox=
45194 loadTiles: function loadTiles(projection, callback) {
45195 if (_off) return; // determine the needed tiles to cover the view
45197 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); // abort inflight requests that are no longer needed
45199 var hadRequests = hasInflightRequests(_tileCache);
45200 abortUnwantedRequests$3(_tileCache, tiles);
45202 if (hadRequests && !hasInflightRequests(_tileCache)) {
45203 dispatch$6.call('loaded'); // stop the spinner
45204 } // issue new requests..
45207 tiles.forEach(function (tile) {
45208 this.loadTile(tile, callback);
45211 // Load a single data tile
45212 // GET /api/0.6/map?bbox=
45213 loadTile: function loadTile(tile, callback) {
45215 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
45217 if (!hasInflightRequests(_tileCache)) {
45218 dispatch$6.call('loading'); // start the spinner
45221 var path = '/api/0.6/map.json?bbox=';
45225 _tileCache.inflight[tile.id] = this.loadFromAPI(path + tile.extent.toParam(), tileCallback, options);
45227 function tileCallback(err, parsed) {
45228 delete _tileCache.inflight[tile.id];
45231 delete _tileCache.toLoad[tile.id];
45232 _tileCache.loaded[tile.id] = true;
45233 var bbox = tile.extent.bbox();
45236 _tileCache.rtree.insert(bbox);
45240 callback(err, Object.assign({
45245 if (!hasInflightRequests(_tileCache)) {
45246 dispatch$6.call('loaded'); // stop the spinner
45250 isDataLoaded: function isDataLoaded(loc) {
45257 return _tileCache.rtree.collides(bbox);
45259 // load the tile that covers the given `loc`
45260 loadTileAtLoc: function loadTileAtLoc(loc, callback) {
45261 // Back off if the toLoad queue is filling up.. re #6417
45262 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
45263 // let users safely edit geometries which extend to unloaded tiles. We can drop some.)
45264 if (Object.keys(_tileCache.toLoad).length > 50) return;
45265 var k = geoZoomToScale(_tileZoom$3 + 1);
45266 var offset = geoRawMercator().scale(k)(loc);
45267 var projection = geoRawMercator().transform({
45272 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
45273 tiles.forEach(function (tile) {
45274 if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
45275 _tileCache.toLoad[tile.id] = true;
45276 this.loadTile(tile, callback);
45279 // Load notes from the API in tiles
45280 // GET /api/0.6/notes?bbox=
45281 loadNotes: function loadNotes(projection, noteOptions) {
45282 noteOptions = Object.assign({
45288 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
45290 var throttleLoadUsers = throttle(function () {
45291 var uids = Object.keys(_userCache.toLoad);
45292 if (!uids.length) return;
45293 that.loadUsers(uids, function () {}); // eagerly load user details
45294 }, 750); // determine the needed tiles to cover the view
45297 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); // abort inflight requests that are no longer needed
45299 abortUnwantedRequests$3(_noteCache, tiles); // issue new requests..
45301 tiles.forEach(function (tile) {
45302 if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
45306 _noteCache.inflight[tile.id] = that.loadFromAPI(path + tile.extent.toParam(), function (err) {
45307 delete _noteCache.inflight[tile.id];
45310 _noteCache.loaded[tile.id] = true;
45313 throttleLoadUsers();
45314 dispatch$6.call('loadedNotes');
45319 // POST /api/0.6/notes?params
45320 postNoteCreate: function postNoteCreate(note, callback) {
45321 if (!this.authenticated()) {
45323 message: 'Not Authenticated',
45328 if (_noteCache.inflightPost[note.id]) {
45330 message: 'Note update already inflight',
45335 if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
45337 var comment = note.newComment;
45339 if (note.newCategory && note.newCategory !== 'None') {
45340 comment += ' #' + note.newCategory;
45343 var path = '/api/0.6/notes?' + utilQsString({
45348 _noteCache.inflightPost[note.id] = oauth.xhr({
45351 }, wrapcb(this, done, _connectionID));
45353 function done(err, xml) {
45354 delete _noteCache.inflightPost[note.id];
45357 return callback(err);
45358 } // we get the updated note back, remove from caches and reparse..
45361 this.removeNote(note);
45365 return parseXML(xml, function (err, results) {
45367 return callback(err);
45369 return callback(undefined, results[0]);
45375 // POST /api/0.6/notes/#id/comment?text=comment
45376 // POST /api/0.6/notes/#id/close?text=comment
45377 // POST /api/0.6/notes/#id/reopen?text=comment
45378 postNoteUpdate: function postNoteUpdate(note, newStatus, callback) {
45379 if (!this.authenticated()) {
45381 message: 'Not Authenticated',
45386 if (_noteCache.inflightPost[note.id]) {
45388 message: 'Note update already inflight',
45395 if (note.status !== 'closed' && newStatus === 'closed') {
45397 } else if (note.status !== 'open' && newStatus === 'open') {
45400 action = 'comment';
45401 if (!note.newComment) return; // when commenting, comment required
45404 var path = '/api/0.6/notes/' + note.id + '/' + action;
45406 if (note.newComment) {
45407 path += '?' + utilQsString({
45408 text: note.newComment
45412 _noteCache.inflightPost[note.id] = oauth.xhr({
45415 }, wrapcb(this, done, _connectionID));
45417 function done(err, xml) {
45418 delete _noteCache.inflightPost[note.id];
45421 return callback(err);
45422 } // we get the updated note back, remove from caches and reparse..
45425 this.removeNote(note); // update closed note cache - used to populate `closed:note` changeset tag
45427 if (action === 'close') {
45428 _noteCache.closed[note.id] = true;
45429 } else if (action === 'reopen') {
45430 delete _noteCache.closed[note.id];
45436 return parseXML(xml, function (err, results) {
45438 return callback(err);
45440 return callback(undefined, results[0]);
45445 "switch": function _switch(options) {
45446 urlroot = options.urlroot;
45447 oauth.options(Object.assign({
45449 loading: authLoading,
45453 this.userChangesets(function () {}); // eagerly load user details/changesets
45455 dispatch$6.call('change');
45458 toggle: function toggle(val) {
45462 isChangesetInflight: function isChangesetInflight() {
45463 return !!_changeset.inflight;
45465 // get/set cached data
45466 // This is used to save/restore the state when entering/exiting the walkthrough
45467 // Also used for testing purposes.
45468 caches: function caches(obj) {
45469 function cloneCache(source) {
45471 Object.keys(source).forEach(function (k) {
45472 if (k === 'rtree') {
45473 target.rtree = new RBush().fromJSON(source.rtree.toJSON()); // clone rbush
45474 } else if (k === 'note') {
45476 Object.keys(source.note).forEach(function (id) {
45477 target.note[id] = osmNote(source.note[id]); // copy notes
45480 target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep
45486 if (!arguments.length) {
45488 tile: cloneCache(_tileCache),
45489 note: cloneCache(_noteCache),
45490 user: cloneCache(_userCache)
45492 } // access caches directly for testing (e.g., loading notes rtree)
45495 if (obj === 'get') {
45504 _tileCache = obj.tile;
45505 _tileCache.inflight = {};
45509 _noteCache = obj.note;
45510 _noteCache.inflight = {};
45511 _noteCache.inflightPost = {};
45515 _userCache = obj.user;
45520 logout: function logout() {
45521 _userChangesets = undefined;
45522 _userDetails = undefined;
45524 dispatch$6.call('change');
45527 authenticated: function authenticated() {
45528 return oauth.authenticated();
45530 authenticate: function authenticate(callback) {
45532 var cid = _connectionID;
45533 _userChangesets = undefined;
45534 _userDetails = undefined;
45536 function done(err, res) {
45538 if (callback) callback(err);
45542 if (that.getConnectionId() !== cid) {
45543 if (callback) callback({
45544 message: 'Connection Switched',
45550 _rateLimitError = undefined;
45551 dispatch$6.call('change');
45552 if (callback) callback(err, res);
45553 that.userChangesets(function () {}); // eagerly load user details/changesets
45556 return oauth.authenticate(done);
45558 imageryBlocklists: function imageryBlocklists() {
45559 return _imageryBlocklists;
45561 tileZoom: function tileZoom(val) {
45562 if (!arguments.length) return _tileZoom$3;
45566 // get all cached notes covering the viewport
45567 notes: function notes(projection) {
45568 var viewport = projection.clipExtent();
45569 var min = [viewport[0][0], viewport[1][1]];
45570 var max = [viewport[1][0], viewport[0][1]];
45571 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
45572 return _noteCache.rtree.search(bbox).map(function (d) {
45576 // get a single note from the cache
45577 getNote: function getNote(id) {
45578 return _noteCache.note[id];
45580 // remove a single note from the cache
45581 removeNote: function removeNote(note) {
45582 if (!(note instanceof osmNote) || !note.id) return;
45583 delete _noteCache.note[note.id];
45584 updateRtree$3(encodeNoteRtree(note), false); // false = remove
45586 // replace a single note in the cache
45587 replaceNote: function replaceNote(note) {
45588 if (!(note instanceof osmNote) || !note.id) return;
45589 _noteCache.note[note.id] = note;
45590 updateRtree$3(encodeNoteRtree(note), true); // true = replace
45594 // Get an array of note IDs closed during this session.
45595 // Used to populate `closed:note` changeset tag
45596 getClosedIDs: function getClosedIDs() {
45597 return Object.keys(_noteCache.closed).sort();
45601 var _apibase = 'https://wiki.openstreetmap.org/w/api.php';
45602 var _inflight$1 = {};
45603 var _wikibaseCache = {};
45608 var debouncedRequest = debounce(request, 500, {
45612 function request(url, callback) {
45613 if (_inflight$1[url]) return;
45614 var controller = new AbortController();
45615 _inflight$1[url] = controller;
45617 signal: controller.signal
45618 }).then(function (result) {
45619 delete _inflight$1[url];
45620 if (callback) callback(null, result);
45621 })["catch"](function (err) {
45622 delete _inflight$1[url];
45623 if (err.name === 'AbortError') return;
45624 if (callback) callback(err.message);
45628 var serviceOsmWikibase = {
45629 init: function init() {
45631 _wikibaseCache = {};
45634 reset: function reset() {
45635 Object.values(_inflight$1).forEach(function (controller) {
45636 controller.abort();
45642 * Get the best value for the property, or undefined if not found
45643 * @param entity object from wikibase
45644 * @param property string e.g. 'P4' for image
45645 * @param langCode string e.g. 'fr' for French
45647 claimToValue: function claimToValue(entity, property, langCode) {
45648 if (!entity.claims[property]) return undefined;
45649 var locale = _localeIDs[langCode];
45650 var preferredPick, localePick;
45651 entity.claims[property].forEach(function (stmt) {
45652 // If exists, use value limited to the needed language (has a qualifier P26 = locale)
45653 // Or if not found, use the first value with the "preferred" rank
45654 if (!preferredPick && stmt.rank === 'preferred') {
45655 preferredPick = stmt;
45658 if (locale && stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === locale) {
45662 var result = localePick || preferredPick;
45665 var datavalue = result.mainsnak.datavalue;
45666 return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
45673 * Convert monolingual property into a key-value object (language -> value)
45674 * @param entity object from wikibase
45675 * @param property string e.g. 'P31' for monolingual wiki page title
45677 monolingualClaimToValueObj: function monolingualClaimToValueObj(entity, property) {
45678 if (!entity || !entity.claims[property]) return undefined;
45679 return entity.claims[property].reduce(function (acc, obj) {
45680 var value = obj.mainsnak.datavalue.value;
45681 acc[value.language] = value.text;
45685 toSitelink: function toSitelink(key, value) {
45686 var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
45687 return result.replace(/_/g, ' ').trim();
45690 // Pass params object of the form:
45693 // value: 'string',
45694 // langCode: 'string'
45697 getEntity: function getEntity(params, callback) {
45698 var doRequest = params.debounce ? debouncedRequest : request;
45702 var rtypeSitelink = params.key === 'type' && params.value ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
45703 var keySitelink = params.key ? this.toSitelink(params.key) : false;
45704 var tagSitelink = params.key && params.value ? this.toSitelink(params.key, params.value) : false;
45705 var localeSitelink;
45707 if (params.langCodes) {
45708 params.langCodes.forEach(function (langCode) {
45709 if (_localeIDs[langCode] === undefined) {
45710 // If this is the first time we are asking about this locale,
45711 // fetch corresponding entity (if it exists), and cache it.
45712 // If there is no such entry, cache `false` value to avoid re-requesting it.
45713 localeSitelink = ('Locale:' + langCode).replace(/_/g, ' ').trim();
45714 titles.push(localeSitelink);
45719 if (rtypeSitelink) {
45720 if (_wikibaseCache[rtypeSitelink]) {
45721 result.rtype = _wikibaseCache[rtypeSitelink];
45723 titles.push(rtypeSitelink);
45728 if (_wikibaseCache[keySitelink]) {
45729 result.key = _wikibaseCache[keySitelink];
45731 titles.push(keySitelink);
45736 if (_wikibaseCache[tagSitelink]) {
45737 result.tag = _wikibaseCache[tagSitelink];
45739 titles.push(tagSitelink);
45743 if (!titles.length) {
45744 // Nothing to do, we already had everything in the cache
45745 return callback(null, result);
45746 } // Requesting just the user language code
45747 // If backend recognizes the code, it will perform proper fallbacks,
45748 // and the result will contain the requested code. If not, all values are returned:
45749 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
45750 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
45754 action: 'wbgetentities',
45756 titles: titles.join('|'),
45757 languages: params.langCodes.join('|'),
45758 languagefallback: 1,
45760 format: 'json' // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
45761 // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
45762 // formatversion: 2,
45765 var url = _apibase + '?' + utilQsString(obj);
45766 doRequest(url, function (err, d) {
45769 } else if (!d.success || d.error) {
45770 callback(d.error.messages.map(function (v) {
45771 return v.html['*'];
45774 var localeID = false;
45775 Object.values(d.entities).forEach(function (res) {
45776 if (res.missing !== '') {
45777 var title = res.sitelinks.wiki.title;
45779 if (title === rtypeSitelink) {
45780 _wikibaseCache[rtypeSitelink] = res;
45781 result.rtype = res;
45782 } else if (title === keySitelink) {
45783 _wikibaseCache[keySitelink] = res;
45785 } else if (title === tagSitelink) {
45786 _wikibaseCache[tagSitelink] = res;
45788 } else if (title === localeSitelink) {
45791 console.log('Unexpected title ' + title); // eslint-disable-line no-console
45796 if (localeSitelink) {
45797 // If locale ID is not found, store false to prevent repeated queries
45798 that.addLocale(params.langCodes[0], localeID);
45801 callback(null, result);
45806 // Pass params object of the form:
45808 // key: 'string', // required
45809 // value: 'string' // optional
45812 // Get an result object used to display tag documentation
45814 // title: 'string',
45815 // description: 'string',
45816 // editURL: 'string',
45817 // imageURL: 'string',
45818 // wiki: { title: 'string', text: 'string', url: 'string' }
45821 getDocs: function getDocs(params, callback) {
45823 var langCodes = _mainLocalizer.localeCodes().map(function (code) {
45824 return code.toLowerCase();
45826 params.langCodes = langCodes;
45827 this.getEntity(params, function (err, data) {
45833 var entity = data.rtype || data.tag || data.key;
45836 callback('No entity');
45843 for (i in langCodes) {
45844 var _code = langCodes[i];
45846 if (entity.descriptions[_code] && entity.descriptions[_code].language === _code) {
45847 description = entity.descriptions[_code];
45852 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
45855 title: entity.title,
45856 description: description ? description.value : '',
45857 descriptionLocaleCode: description ? description.language : '',
45858 editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
45861 if (entity.claims) {
45863 var image = that.claimToValue(entity, 'P4', langCodes[0]);
45866 imageroot = 'https://commons.wikimedia.org/w/index.php';
45868 image = that.claimToValue(entity, 'P28', langCodes[0]);
45871 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
45875 if (imageroot && image) {
45876 result.imageURL = imageroot + '?' + utilQsString({
45877 title: 'Special:Redirect/file/' + image,
45881 } // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
45882 // If neither tag nor key data item contain a wiki page in the needed language nor English,
45883 // get the first found wiki page from either the tag or the key item.
45886 var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
45887 var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
45888 var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
45889 var wikis = [rtypeWiki, tagWiki, keyWiki];
45892 var wiki = wikis[i];
45894 for (var j in langCodes) {
45895 var code = langCodes[j];
45896 var referenceId = langCodes[0].split('-')[0] !== 'en' && code.split('-')[0] === 'en' ? 'inspector.wiki_en_reference' : 'inspector.wiki_reference';
45897 var info = getWikiInfo(wiki, code, referenceId);
45900 result.wiki = info;
45905 if (result.wiki) break;
45908 callback(null, result); // Helper method to get wiki info if a given language exists
45910 function getWikiInfo(wiki, langCode, tKey) {
45911 if (wiki && wiki[langCode]) {
45913 title: wiki[langCode],
45915 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
45921 addLocale: function addLocale(langCode, qid) {
45922 // Makes it easier to unit test
45923 _localeIDs[langCode] = qid;
45925 apibase: function apibase(val) {
45926 if (!arguments.length) return _apibase;
45932 var jsonpCache = {};
45933 window.jsonpCache = jsonpCache;
45934 function jsonpRequest(url, callback) {
45936 abort: function abort() {}
45939 if (window.JSONP_FIX) {
45940 if (window.JSONP_DELAY === 0) {
45941 callback(window.JSONP_FIX);
45943 var t = window.setTimeout(function () {
45944 callback(window.JSONP_FIX);
45945 }, window.JSONP_DELAY || 0);
45947 request.abort = function () {
45948 window.clearTimeout(t);
45956 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
45961 c += chars.charAt(Math.floor(Math.random() * 52));
45967 function create(url) {
45968 var e = url.match(/callback=(\w+)/);
45969 var c = e ? e[1] : rand();
45971 jsonpCache[c] = function (data) {
45972 if (jsonpCache[c]) {
45979 function finalize() {
45980 delete jsonpCache[c];
45984 request.abort = finalize;
45985 return 'jsonpCache.' + c;
45988 var cb = create(url);
45989 var script = select('head').append('script').attr('type', 'text/javascript').attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
45993 var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
45994 var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
45995 var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
45996 var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
45997 var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
45998 var maxResults$2 = 2000;
45999 var tileZoom$2 = 16.5;
46000 var tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
46001 var dispatch$7 = dispatch('loadedImages', 'viewerChanged');
46002 var minHfov = 10; // zoom in degrees: 20, 10, 5
46004 var maxHfov = 90; // zoom out degrees
46006 var defaultHfov = 45;
46007 var _hires = false;
46008 var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
46010 var _currScene = 0;
46014 var _pannellumViewer;
46016 var _sceneOptions = {
46017 showFullscreenCtrl: false,
46028 var _loadViewerPromise$2;
46034 function abortRequest$6(i) {
46038 * localeTimeStamp().
46042 function localeTimestamp(s) {
46043 if (!s) return null;
46049 var d = new Date(s);
46050 if (isNaN(d.getTime())) return null;
46051 return d.toLocaleString(_mainLocalizer.localeCode(), options);
46054 * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
46058 function loadTiles$2(which, url, projection, margin) {
46059 var tiles = tiler$6.margin(margin).getTiles(projection); // abort inflight requests that are no longer needed
46061 var cache = _ssCache[which];
46062 Object.keys(cache.inflight).forEach(function (k) {
46063 var wanted = tiles.find(function (tile) {
46064 return k.indexOf(tile.id + ',') === 0;
46068 abortRequest$6(cache.inflight[k]);
46069 delete cache.inflight[k];
46072 tiles.forEach(function (tile) {
46073 return loadNextTilePage$2(which, url, tile);
46077 * loadNextTilePage() load data for the next tile page in line.
46081 function loadNextTilePage$2(which, url, tile) {
46082 var cache = _ssCache[which];
46083 var nextPage = cache.nextPage[tile.id] || 0;
46084 var id = tile.id + ',' + String(nextPage);
46085 if (cache.loaded[id] || cache.inflight[id]) return;
46086 cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
46087 cache.loaded[id] = true;
46088 delete cache.inflight[id];
46089 if (!bubbles) return; // [].shift() removes the first element, some statistics info, not a bubble point
46092 var features = bubbles.map(function (bubble) {
46093 if (cache.points[bubble.id]) return null; // skip duplicates
46095 var loc = [bubble.lo, bubble.la];
46100 captured_at: bubble.cd,
46101 captured_by: 'microsoft',
46102 // nbn: bubble.nbn,
46103 // pbn: bubble.pbn,
46113 cache.points[bubble.id] = d; // a sequence starts here
46115 if (bubble.pr === undefined) {
46116 cache.leaders.push(bubble.id);
46126 }).filter(Boolean);
46127 cache.rtree.load(features);
46128 connectSequences();
46130 if (which === 'bubbles') {
46131 dispatch$7.call('loadedImages');
46134 } // call this sometimes to connect the bubbles into sequences
46137 function connectSequences() {
46138 var cache = _ssCache.bubbles;
46139 var keepLeaders = [];
46141 for (var i = 0; i < cache.leaders.length; i++) {
46142 var bubble = cache.points[cache.leaders[i]];
46143 var seen = {}; // try to make a sequence.. use the key of the leader bubble.
46149 var complete = false;
46152 sequence.bubbles.push(bubble);
46153 seen[bubble.key] = true;
46155 if (bubble.ne === undefined) {
46158 bubble = cache.points[bubble.ne]; // advance to next
46160 } while (bubble && !seen[bubble.key] && !complete);
46163 _ssCache.sequences[sequence.key] = sequence; // assign bubbles to the sequence
46165 for (var j = 0; j < sequence.bubbles.length; j++) {
46166 sequence.bubbles[j].sequenceKey = sequence.key;
46167 } // create a GeoJSON LineString
46170 sequence.geojson = {
46171 type: 'LineString',
46173 captured_at: sequence.bubbles[0] ? sequence.bubbles[0].captured_at : null,
46174 captured_by: sequence.bubbles[0] ? sequence.bubbles[0].captured_by : null,
46177 coordinates: sequence.bubbles.map(function (d) {
46182 keepLeaders.push(cache.leaders[i]);
46184 } // couldn't complete these, save for later
46187 cache.leaders = keepLeaders;
46190 * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
46194 function getBubbles(url, tile, callback) {
46195 var rect = tile.extent.rectangle();
46196 var urlForRequest = url + utilQsString({
46202 appkey: bubbleAppKey,
46203 jsCallback: '{callback}'
46205 return jsonpRequest(urlForRequest, function (data) {
46206 if (!data || data.error) {
46212 } // partition viewport into higher zoom tiles
46215 function partitionViewport$2(projection) {
46216 var z = geoScaleToZoom(projection.scale());
46217 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
46219 var tiler = utilTiler().zoomExtent([z2, z2]);
46220 return tiler.getTiles(projection).map(function (tile) {
46221 return tile.extent;
46223 } // no more than `limit` results per partition.
46226 function searchLimited$2(limit, projection, rtree) {
46227 limit = limit || 5;
46228 return partitionViewport$2(projection).reduce(function (result, extent) {
46229 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
46232 return found.length ? result.concat(found) : result;
46240 function loadImage(imgInfo) {
46241 return new Promise(function (resolve) {
46242 var img = new Image();
46244 img.onload = function () {
46245 var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
46246 var ctx = canvas.getContext('2d');
46247 ctx.drawImage(img, imgInfo.x, imgInfo.y);
46254 img.onerror = function () {
46261 img.setAttribute('crossorigin', '');
46262 img.src = imgInfo.url;
46270 function loadCanvas(imageGroup) {
46271 return Promise.all(imageGroup.map(loadImage)).then(function (data) {
46272 var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
46281 var face = data[0].imgInfo.face;
46282 _sceneOptions.cubeMap[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
46284 status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'
46293 function loadFaces(faceGroup) {
46294 return Promise.all(faceGroup.map(loadCanvas)).then(function () {
46296 status: 'loadFaces done'
46301 function setupCanvas(selection, reset) {
46303 selection.selectAll('#ideditor-stitcher-canvases').remove();
46304 } // Add the Streetside working canvases. These are used for 'stitching', or combining,
46305 // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
46308 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) {
46309 return 'ideditor-' + d;
46310 }).attr('width', _resolution).attr('height', _resolution);
46313 function qkToXY(qk) {
46318 for (var i = qk.length; i > 0; i--) {
46319 var key = qk[i - 1];
46320 x += +(key === '1' || key === '3') * scale;
46321 y += +(key === '2' || key === '3') * scale;
46328 function getQuadKeys() {
46329 var dim = _resolution / 256;
46333 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'];
46334 } else if (dim === 8) {
46335 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'];
46336 } else if (dim === 4) {
46337 quadKeys = ['00', '01', '10', '11', '02', '03', '12', '13', '20', '21', '30', '31', '22', '23', '32', '33'];
46340 quadKeys = ['0', '1', '2', '3'];
46346 var serviceStreetside = {
46348 * init() initialize streetside.
46350 init: function init() {
46355 this.event = utilRebind(this, dispatch$7, 'on');
46359 * reset() reset the cache.
46361 reset: function reset() {
46363 Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
46371 rtree: new RBush(),
46382 bubbles: function bubbles(projection) {
46384 return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
46386 cachedImage: function cachedImage(imageKey) {
46387 return _ssCache.bubbles.points[imageKey];
46389 sequences: function sequences(projection) {
46390 var viewport = projection.clipExtent();
46391 var min = [viewport[0][0], viewport[1][1]];
46392 var max = [viewport[1][0], viewport[0][1]];
46393 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46395 var results = []; // all sequences for bubbles in viewport
46397 _ssCache.bubbles.rtree.search(bbox).forEach(function (d) {
46398 var key = d.data.sequenceKey;
46400 if (key && !seen[key]) {
46402 results.push(_ssCache.sequences[key].geojson);
46412 loadBubbles: function loadBubbles(projection, margin) {
46413 // by default: request 2 nearby tiles so we can connect sequences.
46414 if (margin === undefined) margin = 2;
46415 loadTiles$2('bubbles', bubbleApi, projection, margin);
46417 viewer: function viewer() {
46418 return _pannellumViewer;
46420 initViewer: function initViewer() {
46421 if (!window.pannellum) return;
46422 if (_pannellumViewer) return;
46425 var sceneID = _currScene.toString();
46429 firstScene: sceneID
46433 options.scenes[sceneID] = _sceneOptions;
46434 _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
46436 ensureViewerLoaded: function ensureViewerLoaded(context) {
46437 if (_loadViewerPromise$2) return _loadViewerPromise$2; // create ms-wrapper, a photo wrapper class
46439 var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper').data([0]); // inject ms-wrapper into the photoviewer div
46440 // (used by all to house each custom photo viewer)
46442 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper ms-wrapper').classed('hide', true);
46444 var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // inject div to support streetside viewer (pannellum) and attribution line
46446 wrapEnter.append('div').attr('id', 'ideditor-viewer-streetside').on(pointerPrefix + 'down.streetside', function () {
46447 select(window).on(pointerPrefix + 'move.streetside', function () {
46448 dispatch$7.call('viewerChanged');
46450 }).on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
46451 select(window).on(pointerPrefix + 'move.streetside', null); // continue dispatching events for a few seconds, in case viewer has inertia.
46453 var t = timer(function (elapsed) {
46454 dispatch$7.call('viewerChanged');
46456 if (elapsed > 2000) {
46460 }).append('div').attr('class', 'photo-attribution fillD');
46461 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
46462 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
46463 controlsEnter.append('button').on('click.forward', step(1)).html('►'); // create working canvas for stitching together images
46465 wrap = wrap.merge(wrapEnter).call(setupCanvas, true); // Register viewer resize handler
46467 context.ui().photoviewer.on('resize.streetside', function () {
46468 if (_pannellumViewer) {
46469 _pannellumViewer.resize();
46472 _loadViewerPromise$2 = new Promise(function (resolve, reject) {
46473 var loadedCount = 0;
46475 function loaded() {
46476 loadedCount += 1; // wait until both files are loaded
46478 if (loadedCount === 2) resolve();
46481 var head = select('head'); // load streetside pannellum viewer css
46483 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 () {
46485 }); // load streetside pannellum viewer js
46487 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 () {
46490 })["catch"](function () {
46491 _loadViewerPromise$2 = null;
46493 return _loadViewerPromise$2;
46495 function step(stepBy) {
46496 return function () {
46497 var viewer = context.container().select('.photoviewer');
46498 var selected = viewer.empty() ? undefined : viewer.datum();
46499 if (!selected) return;
46500 var nextID = stepBy === 1 ? selected.ne : selected.pr;
46502 var yaw = _pannellumViewer.getYaw();
46504 var ca = selected.ca + yaw;
46505 var origin = selected.loc; // construct a search trapezoid pointing out from current bubble
46508 var p1 = [origin[0] + geoMetersToLon(meters / 5, origin[1]), origin[1]];
46509 var p2 = [origin[0] + geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
46510 var p3 = [origin[0] - geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
46511 var p4 = [origin[0] - geoMetersToLon(meters / 5, origin[1]), origin[1]];
46512 var poly = [p1, p2, p3, p4, p1]; // rotate it to face forward/backward
46514 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
46515 poly = geoRotate(poly, -angle, origin);
46516 var extent = poly.reduce(function (extent, point) {
46517 return extent.extend(geoExtent(point));
46518 }, geoExtent()); // find nearest other bubble in the search polygon
46520 var minDist = Infinity;
46522 _ssCache.bubbles.rtree.search(extent.bbox()).forEach(function (d) {
46523 if (d.data.key === selected.key) return;
46524 if (!geoPointInPolygon(d.data.loc, poly)) return;
46525 var dist = geoVecLength(d.data.loc, selected.loc);
46526 var theta = selected.ca - d.data.ca;
46527 var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
46529 if (minTheta > 20) {
46530 dist += 5; // penalize distance if camera angles don't match
46533 if (dist < minDist) {
46534 nextID = d.data.key;
46539 var nextBubble = nextID && that.cachedImage(nextID);
46540 if (!nextBubble) return;
46541 context.map().centerEase(nextBubble.loc);
46542 that.selectImage(context, nextBubble.key).yaw(yaw).showViewer(context);
46546 yaw: function yaw(_yaw) {
46547 if (typeof _yaw !== 'number') return _yaw;
46548 _sceneOptions.yaw = _yaw;
46555 showViewer: function showViewer(context) {
46556 var wrap = context.container().select('.photoviewer').classed('hide', false);
46557 var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
46560 wrap.selectAll('.photo-wrapper:not(.ms-wrapper)').classed('hide', true);
46561 wrap.selectAll('.photo-wrapper.ms-wrapper').classed('hide', false);
46570 hideViewer: function hideViewer(context) {
46571 var viewer = context.container().select('.photoviewer');
46572 if (!viewer.empty()) viewer.datum(null);
46573 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
46574 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
46575 this.updateUrlImage(null);
46576 return this.setStyles(context, null, true);
46582 selectImage: function selectImage(context, key) {
46584 var d = this.cachedImage(key);
46585 var viewer = context.container().select('.photoviewer');
46586 if (!viewer.empty()) viewer.datum(d);
46587 this.setStyles(context, null, true);
46588 var wrap = context.container().select('.photoviewer .ms-wrapper');
46589 var attribution = wrap.selectAll('.photo-attribution').html('');
46590 wrap.selectAll('.pnlm-load-box') // display "loading.."
46591 .style('display', 'block');
46592 if (!d) return this;
46593 this.updateUrlImage(key);
46594 _sceneOptions.northOffset = d.ca;
46595 var line1 = attribution.append('div').attr('class', 'attribution-row');
46596 var hiresDomId = utilUniqueDomId('streetside-hires'); // Add hires checkbox
46598 var label = line1.append('label').attr('for', hiresDomId).attr('class', 'streetside-hires');
46599 label.append('input').attr('type', 'checkbox').attr('id', hiresDomId).property('checked', _hires).on('click', function (d3_event) {
46600 d3_event.stopPropagation();
46602 _resolution = _hires ? 1024 : 512;
46603 wrap.call(setupCanvas, true);
46605 yaw: _pannellumViewer.getYaw(),
46606 pitch: _pannellumViewer.getPitch(),
46607 hfov: _pannellumViewer.getHfov()
46609 _sceneOptions = Object.assign(_sceneOptions, viewstate);
46610 that.selectImage(context, d.key).showViewer(context);
46612 label.append('span').html(_t.html('streetside.hires'));
46613 var captureInfo = line1.append('div').attr('class', 'attribution-capture-info'); // Add capture date
46615 if (d.captured_by) {
46616 var yyyy = new Date().getFullYear();
46617 captureInfo.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://www.microsoft.com/en-us/maps/streetside').html('©' + yyyy + ' Microsoft');
46618 captureInfo.append('span').html('|');
46621 if (d.captured_at) {
46622 captureInfo.append('span').attr('class', 'captured_at').html(localeTimestamp(d.captured_at));
46623 } // Add image links
46626 var line2 = attribution.append('div').attr('class', 'attribution-row');
46627 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'));
46628 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'));
46629 var bubbleIdQuadKey = d.key.toString(4);
46630 var paddingNeeded = 16 - bubbleIdQuadKey.length;
46632 for (var i = 0; i < paddingNeeded; i++) {
46633 bubbleIdQuadKey = '0' + bubbleIdQuadKey;
46636 var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
46637 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
46639 var faceKeys = ['01', '02', '03', '10', '11', '12']; // Map images to cube faces
46641 var quadKeys = getQuadKeys();
46642 var faces = faceKeys.map(function (faceKey) {
46643 return quadKeys.map(function (quadKey) {
46644 var xy = qkToXY(quadKey);
46647 url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
46653 loadFaces(faces).then(function () {
46654 if (!_pannellumViewer) {
46657 // make a new scene
46660 var sceneID = _currScene.toString();
46662 _pannellumViewer.addScene(sceneID, _sceneOptions).loadScene(sceneID); // remove previous scene
46665 if (_currScene > 2) {
46666 sceneID = (_currScene - 1).toString();
46668 _pannellumViewer.removeScene(sceneID);
46674 getSequenceKeyForBubble: function getSequenceKeyForBubble(d) {
46675 return d && d.sequenceKey;
46677 // Updates the currently highlighted sequence and selected bubble.
46678 // Reset is only necessary when interacting with the viewport because
46679 // this implicitly changes the currently selected bubble/sequence
46680 setStyles: function setStyles(context, hovered, reset) {
46682 // reset all layers
46683 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
46684 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
46687 var hoveredBubbleKey = hovered && hovered.key;
46688 var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
46689 var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
46690 var hoveredBubbleKeys = hoveredSequence && hoveredSequence.bubbles.map(function (d) {
46693 var viewer = context.container().select('.photoviewer');
46694 var selected = viewer.empty() ? undefined : viewer.datum();
46695 var selectedBubbleKey = selected && selected.key;
46696 var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
46697 var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
46698 var selectedBubbleKeys = selectedSequence && selectedSequence.bubbles.map(function (d) {
46700 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
46702 var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
46703 context.container().selectAll('.layer-streetside-images .viewfield-group').classed('highlighted', function (d) {
46704 return highlightedBubbleKeys.indexOf(d.key) !== -1;
46705 }).classed('hovered', function (d) {
46706 return d.key === hoveredBubbleKey;
46707 }).classed('currentView', function (d) {
46708 return d.key === selectedBubbleKey;
46710 context.container().selectAll('.layer-streetside-images .sequence').classed('highlighted', function (d) {
46711 return d.properties.key === hoveredSequenceKey;
46712 }).classed('currentView', function (d) {
46713 return d.properties.key === selectedSequenceKey;
46714 }); // update viewfields if needed
46716 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
46718 function viewfieldPath() {
46719 var d = this.parentNode.__data__;
46721 if (d.pano && d.key !== selectedBubbleKey) {
46722 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
46724 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
46730 updateUrlImage: function updateUrlImage(imageKey) {
46731 if (!window.mocha) {
46732 var hash = utilStringQs(window.location.hash);
46735 hash.photo = 'streetside/' + imageKey;
46740 window.location.replace('#' + utilQsString(hash, true));
46747 cache: function cache() {
46752 var _apibase$1 = 'https://taginfo.openstreetmap.org/api/4/';
46753 var _inflight$2 = {};
46754 var _popularKeys = {};
46755 var _taginfoCache = {};
46757 point: 'count_nodes',
46758 vertex: 'count_nodes',
46759 area: 'count_ways',
46762 var tag_sort_members = {
46763 point: 'count_node_members',
46764 vertex: 'count_node_members',
46765 area: 'count_way_members',
46766 line: 'count_way_members',
46767 relation: 'count_relation_members'
46769 var tag_filters = {
46775 var tag_members_fractions = {
46776 point: 'count_node_members_fraction',
46777 vertex: 'count_node_members_fraction',
46778 area: 'count_way_members_fraction',
46779 line: 'count_way_members_fraction',
46780 relation: 'count_relation_members_fraction'
46783 function sets(params, n, o) {
46784 if (params.geometry && o[params.geometry]) {
46785 params[n] = o[params.geometry];
46791 function setFilter(params) {
46792 return sets(params, 'filter', tag_filters);
46795 function setSort(params) {
46796 return sets(params, 'sortname', tag_sorts);
46799 function setSortMembers(params) {
46800 return sets(params, 'sortname', tag_sort_members);
46803 function clean(params) {
46804 return utilObjectOmit(params, ['geometry', 'debounce']);
46807 function filterKeys(type) {
46808 var count_type = type ? 'count_' + type : 'count_all';
46809 return function (d) {
46810 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
46814 function filterMultikeys(prefix) {
46815 return function (d) {
46816 // d.key begins with prefix, and d.key contains no additional ':'s
46817 var re = new RegExp('^' + prefix + '(.*)$');
46818 var matches = d.key.match(re) || [];
46819 return matches.length === 2 && matches[1].indexOf(':') === -1;
46823 function filterValues(allowUpperCase) {
46824 return function (d) {
46825 if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation
46827 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters
46829 return parseFloat(d.fraction) > 0.0;
46833 function filterRoles(geometry) {
46834 return function (d) {
46835 if (d.role === '') return false; // exclude empty role
46837 if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation
46839 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
46843 function valKey(d) {
46850 function valKeyDescription(d) {
46853 title: d.description || d.value
46857 obj.count = d.count;
46863 function roleKey(d) {
46868 } // sort keys with ':' lower than keys without ':'
46871 function sortKeys(a, b) {
46872 return a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1 ? -1 : a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1 ? 1 : 0;
46875 var debouncedRequest$1 = debounce(request$1, 300, {
46879 function request$1(url, params, exactMatch, callback, loaded) {
46880 if (_inflight$2[url]) return;
46881 if (checkCache(url, params, exactMatch, callback)) return;
46882 var controller = new AbortController();
46883 _inflight$2[url] = controller;
46885 signal: controller.signal
46886 }).then(function (result) {
46887 delete _inflight$2[url];
46888 if (loaded) loaded(null, result);
46889 })["catch"](function (err) {
46890 delete _inflight$2[url];
46891 if (err.name === 'AbortError') return;
46892 if (loaded) loaded(err.message);
46896 function checkCache(url, params, exactMatch, callback) {
46897 var rp = params.rp || 25;
46898 var testQuery = params.query || '';
46902 var hit = _taginfoCache[testUrl]; // exact match, or shorter match yielding fewer than max results (rp)
46904 if (hit && (url === testUrl || hit.length < rp)) {
46905 callback(null, hit);
46907 } // don't try to shorten the query
46910 if (exactMatch || !testQuery.length) return false; // do shorten the query to see if we already have a cached result
46911 // that has returned fewer than max results (rp)
46913 testQuery = testQuery.slice(0, -1);
46914 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
46915 } while (testQuery.length >= 0);
46920 var serviceTaginfo = {
46921 init: function init() {
46923 _taginfoCache = {};
46925 // manually exclude some keys – #5377, #7485
46931 sorting_name: true,
46935 'bridge:name': true
46936 }; // Fetch popular keys. We'll exclude these from `values`
46937 // lookups because they stress taginfo, and they aren't likely
46938 // to yield meaningful autocomplete results.. see #3955
46942 sortname: 'values_all',
46946 lang: _mainLocalizer.languageCode()
46948 this.keys(params, function (err, data) {
46950 data.forEach(function (d) {
46951 if (d.value === 'opening_hours') return; // exception
46953 _popularKeys[d.value] = true;
46957 reset: function reset() {
46958 Object.values(_inflight$2).forEach(function (controller) {
46959 controller.abort();
46963 keys: function keys(params, callback) {
46964 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
46965 params = clean(setSort(params));
46966 params = Object.assign({
46968 sortname: 'count_all',
46971 lang: _mainLocalizer.languageCode()
46973 var url = _apibase$1 + 'keys/all?' + utilQsString(params);
46974 doRequest(url, params, false, callback, function (err, d) {
46978 var f = filterKeys(params.filter);
46979 var result = d.data.filter(f).sort(sortKeys).map(valKey);
46980 _taginfoCache[url] = result;
46981 callback(null, result);
46985 multikeys: function multikeys(params, callback) {
46986 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
46987 params = clean(setSort(params));
46988 params = Object.assign({
46990 sortname: 'count_all',
46993 lang: _mainLocalizer.languageCode()
46995 var prefix = params.query;
46996 var url = _apibase$1 + 'keys/all?' + utilQsString(params);
46997 doRequest(url, params, true, callback, function (err, d) {
47001 var f = filterMultikeys(prefix);
47002 var result = d.data.filter(f).map(valKey);
47003 _taginfoCache[url] = result;
47004 callback(null, result);
47008 values: function values(params, callback) {
47009 // Exclude popular keys from values lookups.. see #3955
47010 var key = params.key;
47012 if (key && _popularKeys[key]) {
47013 callback(null, []);
47017 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47018 params = clean(setSort(setFilter(params)));
47019 params = Object.assign({
47021 sortname: 'count_all',
47024 lang: _mainLocalizer.languageCode()
47026 var url = _apibase$1 + 'key/values?' + utilQsString(params);
47027 doRequest(url, params, false, callback, function (err, d) {
47031 // In most cases we prefer taginfo value results with lowercase letters.
47032 // A few OSM keys expect values to contain uppercase values (see #3377).
47033 // This is not an exhaustive list (e.g. `name` also has uppercase values)
47034 // but these are the fields where taginfo value lookup is most useful.
47035 var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
47036 var allowUpperCase = re.test(params.key);
47037 var f = filterValues(allowUpperCase);
47038 var result = d.data.filter(f).map(valKeyDescription);
47039 _taginfoCache[url] = result;
47040 callback(null, result);
47044 roles: function roles(params, callback) {
47045 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47046 var geometry = params.geometry;
47047 params = clean(setSortMembers(params));
47048 params = Object.assign({
47050 sortname: 'count_all_members',
47053 lang: _mainLocalizer.languageCode()
47055 var url = _apibase$1 + 'relation/roles?' + utilQsString(params);
47056 doRequest(url, params, true, callback, function (err, d) {
47060 var f = filterRoles(geometry);
47061 var result = d.data.filter(f).map(roleKey);
47062 _taginfoCache[url] = result;
47063 callback(null, result);
47067 docs: function docs(params, callback) {
47068 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47069 params = clean(setSort(params));
47070 var path = 'key/wiki_pages?';
47072 if (params.value) {
47073 path = 'tag/wiki_pages?';
47074 } else if (params.rtype) {
47075 path = 'relation/wiki_pages?';
47078 var url = _apibase$1 + path + utilQsString(params);
47079 doRequest(url, params, true, callback, function (err, d) {
47083 _taginfoCache[url] = d.data;
47084 callback(null, d.data);
47088 apibase: function apibase(_) {
47089 if (!arguments.length) return _apibase$1;
47095 var helpers$1 = createCommonjsModule(function (module, exports) {
47097 Object.defineProperty(exports, "__esModule", {
47105 * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
47107 * @memberof helpers
47111 exports.earthRadius = 6371008.8;
47113 * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
47115 * @memberof helpers
47119 exports.factors = {
47120 centimeters: exports.earthRadius * 100,
47121 centimetres: exports.earthRadius * 100,
47122 degrees: exports.earthRadius / 111325,
47123 feet: exports.earthRadius * 3.28084,
47124 inches: exports.earthRadius * 39.370,
47125 kilometers: exports.earthRadius / 1000,
47126 kilometres: exports.earthRadius / 1000,
47127 meters: exports.earthRadius,
47128 metres: exports.earthRadius,
47129 miles: exports.earthRadius / 1609.344,
47130 millimeters: exports.earthRadius * 1000,
47131 millimetres: exports.earthRadius * 1000,
47132 nauticalmiles: exports.earthRadius / 1852,
47134 yards: exports.earthRadius / 1.0936
47137 * Units of measurement factors based on 1 meter.
47139 * @memberof helpers
47143 exports.unitsFactors = {
47146 degrees: 1 / 111325,
47149 kilometers: 1 / 1000,
47150 kilometres: 1 / 1000,
47153 miles: 1 / 1609.344,
47156 nauticalmiles: 1 / 1852,
47157 radians: 1 / exports.earthRadius,
47161 * Area of measurement factors based on 1 square meter.
47163 * @memberof helpers
47167 exports.areaFactors = {
47168 acres: 0.000247105,
47169 centimeters: 10000,
47170 centimetres: 10000,
47171 feet: 10.763910417,
47172 inches: 1550.003100006,
47173 kilometers: 0.000001,
47174 kilometres: 0.000001,
47178 millimeters: 1000000,
47179 millimetres: 1000000,
47183 * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
47186 * @param {Geometry} geometry input geometry
47187 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47188 * @param {Object} [options={}] Optional Parameters
47189 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47190 * @param {string|number} [options.id] Identifier associated with the Feature
47191 * @returns {Feature} a GeoJSON Feature
47195 * "coordinates": [110, 50]
47198 * var feature = turf.feature(geometry);
47203 function feature(geom, properties, options) {
47204 if (options === void 0) {
47212 if (options.id === 0 || options.id) {
47213 feat.id = options.id;
47216 if (options.bbox) {
47217 feat.bbox = options.bbox;
47220 feat.properties = properties || {};
47221 feat.geometry = geom;
47225 exports.feature = feature;
47227 * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
47228 * For GeometryCollection type use `helpers.geometryCollection`
47231 * @param {string} type Geometry Type
47232 * @param {Array<any>} coordinates Coordinates
47233 * @param {Object} [options={}] Optional Parameters
47234 * @returns {Geometry} a GeoJSON Geometry
47236 * var type = "Point";
47237 * var coordinates = [110, 50];
47238 * var geometry = turf.geometry(type, coordinates);
47242 function geometry(type, coordinates, options) {
47246 return point(coordinates).geometry;
47249 return lineString(coordinates).geometry;
47252 return polygon(coordinates).geometry;
47255 return multiPoint(coordinates).geometry;
47257 case "MultiLineString":
47258 return multiLineString(coordinates).geometry;
47260 case "MultiPolygon":
47261 return multiPolygon(coordinates).geometry;
47264 throw new Error(type + " is invalid");
47268 exports.geometry = geometry;
47270 * Creates a {@link Point} {@link Feature} from a Position.
47273 * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
47274 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47275 * @param {Object} [options={}] Optional Parameters
47276 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47277 * @param {string|number} [options.id] Identifier associated with the Feature
47278 * @returns {Feature<Point>} a Point feature
47280 * var point = turf.point([-75.343, 39.984]);
47285 function point(coordinates, properties, options) {
47286 if (options === void 0) {
47292 coordinates: coordinates
47294 return feature(geom, properties, options);
47297 exports.point = point;
47299 * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
47302 * @param {Array<Array<number>>} coordinates an array of Points
47303 * @param {Object} [properties={}] Translate these properties to each Feature
47304 * @param {Object} [options={}] Optional Parameters
47305 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
47306 * associated with the FeatureCollection
47307 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47308 * @returns {FeatureCollection<Point>} Point Feature
47310 * var points = turf.points([
47319 function points(coordinates, properties, options) {
47320 if (options === void 0) {
47324 return featureCollection(coordinates.map(function (coords) {
47325 return point(coords, properties);
47329 exports.points = points;
47331 * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
47334 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
47335 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47336 * @param {Object} [options={}] Optional Parameters
47337 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47338 * @param {string|number} [options.id] Identifier associated with the Feature
47339 * @returns {Feature<Polygon>} Polygon Feature
47341 * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
47346 function polygon(coordinates, properties, options) {
47347 if (options === void 0) {
47351 for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
47352 var ring = coordinates_1[_i];
47354 if (ring.length < 4) {
47355 throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
47358 for (var j = 0; j < ring[ring.length - 1].length; j++) {
47359 // Check if first point of Polygon contains two numbers
47360 if (ring[ring.length - 1][j] !== ring[0][j]) {
47361 throw new Error("First and last Position are not equivalent.");
47368 coordinates: coordinates
47370 return feature(geom, properties, options);
47373 exports.polygon = polygon;
47375 * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
47378 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
47379 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47380 * @param {Object} [options={}] Optional Parameters
47381 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47382 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47383 * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
47385 * var polygons = turf.polygons([
47386 * [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
47387 * [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
47393 function polygons(coordinates, properties, options) {
47394 if (options === void 0) {
47398 return featureCollection(coordinates.map(function (coords) {
47399 return polygon(coords, properties);
47403 exports.polygons = polygons;
47405 * Creates a {@link LineString} {@link Feature} from an Array of Positions.
47408 * @param {Array<Array<number>>} coordinates an array of Positions
47409 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47410 * @param {Object} [options={}] Optional Parameters
47411 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47412 * @param {string|number} [options.id] Identifier associated with the Feature
47413 * @returns {Feature<LineString>} LineString Feature
47415 * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
47416 * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
47422 function lineString(coordinates, properties, options) {
47423 if (options === void 0) {
47427 if (coordinates.length < 2) {
47428 throw new Error("coordinates must be an array of two or more positions");
47432 type: "LineString",
47433 coordinates: coordinates
47435 return feature(geom, properties, options);
47438 exports.lineString = lineString;
47440 * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
47442 * @name lineStrings
47443 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
47444 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47445 * @param {Object} [options={}] Optional Parameters
47446 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
47447 * associated with the FeatureCollection
47448 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47449 * @returns {FeatureCollection<LineString>} LineString FeatureCollection
47451 * var linestrings = turf.lineStrings([
47452 * [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
47453 * [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
47459 function lineStrings(coordinates, properties, options) {
47460 if (options === void 0) {
47464 return featureCollection(coordinates.map(function (coords) {
47465 return lineString(coords, properties);
47469 exports.lineStrings = lineStrings;
47471 * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
47473 * @name featureCollection
47474 * @param {Feature[]} features input features
47475 * @param {Object} [options={}] Optional Parameters
47476 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47477 * @param {string|number} [options.id] Identifier associated with the Feature
47478 * @returns {FeatureCollection} FeatureCollection of Features
47480 * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
47481 * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
47482 * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
47484 * var collection = turf.featureCollection([
47493 function featureCollection(features, options) {
47494 if (options === void 0) {
47499 type: "FeatureCollection"
47503 fc.id = options.id;
47506 if (options.bbox) {
47507 fc.bbox = options.bbox;
47510 fc.features = features;
47514 exports.featureCollection = featureCollection;
47516 * Creates a {@link Feature<MultiLineString>} based on a
47517 * coordinate array. Properties can be added optionally.
47519 * @name multiLineString
47520 * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
47521 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47522 * @param {Object} [options={}] Optional Parameters
47523 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47524 * @param {string|number} [options.id] Identifier associated with the Feature
47525 * @returns {Feature<MultiLineString>} a MultiLineString feature
47526 * @throws {Error} if no coordinates are passed
47528 * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
47533 function multiLineString(coordinates, properties, options) {
47534 if (options === void 0) {
47539 type: "MultiLineString",
47540 coordinates: coordinates
47542 return feature(geom, properties, options);
47545 exports.multiLineString = multiLineString;
47547 * Creates a {@link Feature<MultiPoint>} based on a
47548 * coordinate array. Properties can be added optionally.
47551 * @param {Array<Array<number>>} coordinates an array of Positions
47552 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47553 * @param {Object} [options={}] Optional Parameters
47554 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47555 * @param {string|number} [options.id] Identifier associated with the Feature
47556 * @returns {Feature<MultiPoint>} a MultiPoint feature
47557 * @throws {Error} if no coordinates are passed
47559 * var multiPt = turf.multiPoint([[0,0],[10,10]]);
47564 function multiPoint(coordinates, properties, options) {
47565 if (options === void 0) {
47570 type: "MultiPoint",
47571 coordinates: coordinates
47573 return feature(geom, properties, options);
47576 exports.multiPoint = multiPoint;
47578 * Creates a {@link Feature<MultiPolygon>} based on a
47579 * coordinate array. Properties can be added optionally.
47581 * @name multiPolygon
47582 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
47583 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47584 * @param {Object} [options={}] Optional Parameters
47585 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47586 * @param {string|number} [options.id] Identifier associated with the Feature
47587 * @returns {Feature<MultiPolygon>} a multipolygon feature
47588 * @throws {Error} if no coordinates are passed
47590 * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
47596 function multiPolygon(coordinates, properties, options) {
47597 if (options === void 0) {
47602 type: "MultiPolygon",
47603 coordinates: coordinates
47605 return feature(geom, properties, options);
47608 exports.multiPolygon = multiPolygon;
47610 * Creates a {@link Feature<GeometryCollection>} based on a
47611 * coordinate array. Properties can be added optionally.
47613 * @name geometryCollection
47614 * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
47615 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47616 * @param {Object} [options={}] Optional Parameters
47617 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47618 * @param {string|number} [options.id] Identifier associated with the Feature
47619 * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
47621 * var pt = turf.geometry("Point", [100, 0]);
47622 * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
47623 * var collection = turf.geometryCollection([pt, line]);
47628 function geometryCollection(geometries, properties, options) {
47629 if (options === void 0) {
47634 type: "GeometryCollection",
47635 geometries: geometries
47637 return feature(geom, properties, options);
47640 exports.geometryCollection = geometryCollection;
47642 * Round number to precision
47644 * @param {number} num Number
47645 * @param {number} [precision=0] Precision
47646 * @returns {number} rounded number
47648 * turf.round(120.4321)
47651 * turf.round(120.4321, 2)
47655 function round(num, precision) {
47656 if (precision === void 0) {
47660 if (precision && !(precision >= 0)) {
47661 throw new Error("precision must be a positive number");
47664 var multiplier = Math.pow(10, precision || 0);
47665 return Math.round(num * multiplier) / multiplier;
47668 exports.round = round;
47670 * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
47671 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47673 * @name radiansToLength
47674 * @param {number} radians in radians across the sphere
47675 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47676 * meters, kilometres, kilometers.
47677 * @returns {number} distance
47680 function radiansToLength(radians, units) {
47681 if (units === void 0) {
47682 units = "kilometers";
47685 var factor = exports.factors[units];
47688 throw new Error(units + " units is invalid");
47691 return radians * factor;
47694 exports.radiansToLength = radiansToLength;
47696 * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
47697 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47699 * @name lengthToRadians
47700 * @param {number} distance in real units
47701 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47702 * meters, kilometres, kilometers.
47703 * @returns {number} radians
47706 function lengthToRadians(distance, units) {
47707 if (units === void 0) {
47708 units = "kilometers";
47711 var factor = exports.factors[units];
47714 throw new Error(units + " units is invalid");
47717 return distance / factor;
47720 exports.lengthToRadians = lengthToRadians;
47722 * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
47723 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
47725 * @name lengthToDegrees
47726 * @param {number} distance in real units
47727 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47728 * meters, kilometres, kilometers.
47729 * @returns {number} degrees
47732 function lengthToDegrees(distance, units) {
47733 return radiansToDegrees(lengthToRadians(distance, units));
47736 exports.lengthToDegrees = lengthToDegrees;
47738 * Converts any bearing angle from the north line direction (positive clockwise)
47739 * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
47741 * @name bearingToAzimuth
47742 * @param {number} bearing angle, between -180 and +180 degrees
47743 * @returns {number} angle between 0 and 360 degrees
47746 function bearingToAzimuth(bearing) {
47747 var angle = bearing % 360;
47756 exports.bearingToAzimuth = bearingToAzimuth;
47758 * Converts an angle in radians to degrees
47760 * @name radiansToDegrees
47761 * @param {number} radians angle in radians
47762 * @returns {number} degrees between 0 and 360 degrees
47765 function radiansToDegrees(radians) {
47766 var degrees = radians % (2 * Math.PI);
47767 return degrees * 180 / Math.PI;
47770 exports.radiansToDegrees = radiansToDegrees;
47772 * Converts an angle in degrees to radians
47774 * @name degreesToRadians
47775 * @param {number} degrees angle between 0 and 360 degrees
47776 * @returns {number} angle in radians
47779 function degreesToRadians(degrees) {
47780 var radians = degrees % 360;
47781 return radians * Math.PI / 180;
47784 exports.degreesToRadians = degreesToRadians;
47786 * Converts a length to the requested unit.
47787 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47789 * @param {number} length to be converted
47790 * @param {Units} [originalUnit="kilometers"] of the length
47791 * @param {Units} [finalUnit="kilometers"] returned unit
47792 * @returns {number} the converted length
47795 function convertLength(length, originalUnit, finalUnit) {
47796 if (originalUnit === void 0) {
47797 originalUnit = "kilometers";
47800 if (finalUnit === void 0) {
47801 finalUnit = "kilometers";
47804 if (!(length >= 0)) {
47805 throw new Error("length must be a positive number");
47808 return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
47811 exports.convertLength = convertLength;
47813 * Converts a area to the requested unit.
47814 * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
47815 * @param {number} area to be converted
47816 * @param {Units} [originalUnit="meters"] of the distance
47817 * @param {Units} [finalUnit="kilometers"] returned unit
47818 * @returns {number} the converted distance
47821 function convertArea(area, originalUnit, finalUnit) {
47822 if (originalUnit === void 0) {
47823 originalUnit = "meters";
47826 if (finalUnit === void 0) {
47827 finalUnit = "kilometers";
47830 if (!(area >= 0)) {
47831 throw new Error("area must be a positive number");
47834 var startFactor = exports.areaFactors[originalUnit];
47836 if (!startFactor) {
47837 throw new Error("invalid original units");
47840 var finalFactor = exports.areaFactors[finalUnit];
47842 if (!finalFactor) {
47843 throw new Error("invalid final units");
47846 return area / startFactor * finalFactor;
47849 exports.convertArea = convertArea;
47853 * @param {*} num Number to validate
47854 * @returns {boolean} true/false
47856 * turf.isNumber(123)
47858 * turf.isNumber('foo')
47862 function isNumber(num) {
47863 return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
47866 exports.isNumber = isNumber;
47870 * @param {*} input variable to validate
47871 * @returns {boolean} true/false
47873 * turf.isObject({elevation: 10})
47875 * turf.isObject('foo')
47879 function isObject(input) {
47880 return !!input && input.constructor === Object;
47883 exports.isObject = isObject;
47888 * @param {Array<number>} bbox BBox to validate
47890 * @throws Error if BBox is not valid
47892 * validateBBox([-180, -40, 110, 50])
47894 * validateBBox([-180, -40])
47896 * validateBBox('Foo')
47900 * validateBBox(null)
47902 * validateBBox(undefined)
47906 function validateBBox(bbox) {
47908 throw new Error("bbox is required");
47911 if (!Array.isArray(bbox)) {
47912 throw new Error("bbox must be an Array");
47915 if (bbox.length !== 4 && bbox.length !== 6) {
47916 throw new Error("bbox must be an Array of 4 or 6 numbers");
47919 bbox.forEach(function (num) {
47920 if (!isNumber(num)) {
47921 throw new Error("bbox must only contain numbers");
47926 exports.validateBBox = validateBBox;
47931 * @param {string|number} id Id to validate
47933 * @throws Error if Id is not valid
47935 * validateId([-180, -40, 110, 50])
47937 * validateId([-180, -40])
47939 * validateId('Foo')
47945 * validateId(undefined)
47949 function validateId(id) {
47951 throw new Error("id is required");
47954 if (["string", "number"].indexOf(_typeof(id)) === -1) {
47955 throw new Error("id must be a number or a string");
47959 exports.validateId = validateId; // Deprecated methods
47961 function radians2degrees() {
47962 throw new Error("method has been renamed to `radiansToDegrees`");
47965 exports.radians2degrees = radians2degrees;
47967 function degrees2radians() {
47968 throw new Error("method has been renamed to `degreesToRadians`");
47971 exports.degrees2radians = degrees2radians;
47973 function distanceToDegrees() {
47974 throw new Error("method has been renamed to `lengthToDegrees`");
47977 exports.distanceToDegrees = distanceToDegrees;
47979 function distanceToRadians() {
47980 throw new Error("method has been renamed to `lengthToRadians`");
47983 exports.distanceToRadians = distanceToRadians;
47985 function radiansToDistance() {
47986 throw new Error("method has been renamed to `radiansToLength`");
47989 exports.radiansToDistance = radiansToDistance;
47991 function bearingToAngle() {
47992 throw new Error("method has been renamed to `bearingToAzimuth`");
47995 exports.bearingToAngle = bearingToAngle;
47997 function convertDistance() {
47998 throw new Error("method has been renamed to `convertLength`");
48001 exports.convertDistance = convertDistance;
48004 var invariant = createCommonjsModule(function (module, exports) {
48006 Object.defineProperty(exports, "__esModule", {
48010 * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.
48013 * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers
48014 * @returns {Array<number>} coordinates
48016 * var pt = turf.point([10, 10]);
48018 * var coord = turf.getCoord(pt);
48022 function getCoord(coord) {
48024 throw new Error("coord is required");
48027 if (!Array.isArray(coord)) {
48028 if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
48029 return coord.geometry.coordinates;
48032 if (coord.type === "Point") {
48033 return coord.coordinates;
48037 if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
48041 throw new Error("coord must be GeoJSON Point or an Array of numbers");
48044 exports.getCoord = getCoord;
48046 * Unwrap coordinates from a Feature, Geometry Object or an Array
48049 * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array
48050 * @returns {Array<any>} coordinates
48052 * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]);
48054 * var coords = turf.getCoords(poly);
48055 * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]
48058 function getCoords(coords) {
48059 if (Array.isArray(coords)) {
48064 if (coords.type === "Feature") {
48065 if (coords.geometry !== null) {
48066 return coords.geometry.coordinates;
48070 if (coords.coordinates) {
48071 return coords.coordinates;
48075 throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
48078 exports.getCoords = getCoords;
48080 * Checks if coordinates contains a number
48082 * @name containsNumber
48083 * @param {Array<any>} coordinates GeoJSON Coordinates
48084 * @returns {boolean} true if Array contains a number
48087 function containsNumber(coordinates) {
48088 if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) {
48092 if (Array.isArray(coordinates[0]) && coordinates[0].length) {
48093 return containsNumber(coordinates[0]);
48096 throw new Error("coordinates must only contain numbers");
48099 exports.containsNumber = containsNumber;
48101 * Enforce expectations about types of GeoJSON objects for Turf.
48103 * @name geojsonType
48104 * @param {GeoJSON} value any GeoJSON object
48105 * @param {string} type expected GeoJSON type
48106 * @param {string} name name of calling function
48107 * @throws {Error} if value is not the expected type.
48110 function geojsonType(value, type, name) {
48111 if (!type || !name) {
48112 throw new Error("type and name required");
48115 if (!value || value.type !== type) {
48116 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type);
48120 exports.geojsonType = geojsonType;
48122 * Enforce expectations about types of {@link Feature} inputs for Turf.
48123 * Internally this uses {@link geojsonType} to judge geometry types.
48126 * @param {Feature} feature a feature with an expected geometry type
48127 * @param {string} type expected GeoJSON type
48128 * @param {string} name name of calling function
48129 * @throws {Error} error if value is not the expected type.
48132 function featureOf(feature, type, name) {
48134 throw new Error("No feature passed");
48138 throw new Error(".featureOf() requires a name");
48141 if (!feature || feature.type !== "Feature" || !feature.geometry) {
48142 throw new Error("Invalid input to " + name + ", Feature with geometry required");
48145 if (!feature.geometry || feature.geometry.type !== type) {
48146 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
48150 exports.featureOf = featureOf;
48152 * Enforce expectations about types of {@link FeatureCollection} inputs for Turf.
48153 * Internally this uses {@link geojsonType} to judge geometry types.
48155 * @name collectionOf
48156 * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged
48157 * @param {string} type expected GeoJSON type
48158 * @param {string} name name of calling function
48159 * @throws {Error} if value is not the expected type.
48162 function collectionOf(featureCollection, type, name) {
48163 if (!featureCollection) {
48164 throw new Error("No featureCollection passed");
48168 throw new Error(".collectionOf() requires a name");
48171 if (!featureCollection || featureCollection.type !== "FeatureCollection") {
48172 throw new Error("Invalid input to " + name + ", FeatureCollection required");
48175 for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
48176 var feature = _a[_i];
48178 if (!feature || feature.type !== "Feature" || !feature.geometry) {
48179 throw new Error("Invalid input to " + name + ", Feature with geometry required");
48182 if (!feature.geometry || feature.geometry.type !== type) {
48183 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
48188 exports.collectionOf = collectionOf;
48190 * Get Geometry from Feature or Geometry Object
48192 * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
48193 * @returns {Geometry|null} GeoJSON Geometry Object
48194 * @throws {Error} if geojson is not a Feature or Geometry Object
48197 * "type": "Feature",
48198 * "properties": {},
48201 * "coordinates": [110, 40]
48204 * var geom = turf.getGeom(point)
48205 * //={"type": "Point", "coordinates": [110, 40]}
48208 function getGeom(geojson) {
48209 if (geojson.type === "Feature") {
48210 return geojson.geometry;
48216 exports.getGeom = getGeom;
48218 * Get GeoJSON object's type, Geometry type is prioritize.
48220 * @param {GeoJSON} geojson GeoJSON object
48221 * @param {string} [name="geojson"] name of the variable to display in error message
48222 * @returns {string} GeoJSON type
48225 * "type": "Feature",
48226 * "properties": {},
48229 * "coordinates": [110, 40]
48232 * var geom = turf.getType(point)
48236 function getType(geojson, name) {
48237 if (geojson.type === "FeatureCollection") {
48238 return "FeatureCollection";
48241 if (geojson.type === "GeometryCollection") {
48242 return "GeometryCollection";
48245 if (geojson.type === "Feature" && geojson.geometry !== null) {
48246 return geojson.geometry.type;
48249 return geojson.type;
48252 exports.getType = getType;
48255 var lineclip_1 = lineclip;
48256 var _default = lineclip;
48257 lineclip.polyline = lineclip;
48258 lineclip.polygon = polygonclip; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
48259 // handle polylines rather than just segments
48261 function lineclip(points, bbox, result) {
48262 var len = points.length,
48263 codeA = bitCode(points[0], bbox),
48270 if (!result) result = [];
48272 for (i = 1; i < len; i++) {
48275 codeB = lastCode = bitCode(b, bbox);
48278 if (!(codeA | codeB)) {
48282 if (codeB !== lastCode) {
48283 // segment went outside
48287 // start a new line
48291 } else if (i === len - 1) {
48296 } else if (codeA & codeB) {
48299 } else if (codeA) {
48300 // a outside, intersect with clip edge
48301 a = intersect(a, b, codeA, bbox);
48302 codeA = bitCode(a, bbox);
48305 b = intersect(a, b, codeB, bbox);
48306 codeB = bitCode(b, bbox);
48313 if (part.length) result.push(part);
48315 } // Sutherland-Hodgeman polygon clipping algorithm
48318 function polygonclip(points, bbox) {
48319 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
48321 for (edge = 1; edge <= 8; edge *= 2) {
48323 prev = points[points.length - 1];
48324 prevInside = !(bitCode(prev, bbox) & edge);
48326 for (i = 0; i < points.length; i++) {
48328 inside = !(bitCode(p, bbox) & edge); // if segment goes through the clip window, add an intersection
48330 if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
48331 if (inside) result.push(p); // add a point if it's inside
48334 prevInside = inside;
48338 if (!points.length) break;
48342 } // intersect a segment against one of the 4 lines that make up the bbox
48345 function intersect(a, b, edge, bbox) {
48346 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
48347 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
48348 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
48349 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
48351 } // bit code reflects the point position relative to the bbox:
48353 // top 1001 1000 1010
48354 // mid 0001 0000 0010
48355 // bottom 0101 0100 0110
48358 function bitCode(p, bbox) {
48360 if (p[0] < bbox[0]) code |= 1; // left
48361 else if (p[0] > bbox[2]) code |= 2; // right
48363 if (p[1] < bbox[1]) code |= 4; // bottom
48364 else if (p[1] > bbox[3]) code |= 8; // top
48368 lineclip_1["default"] = _default;
48370 var bboxClip_1 = createCommonjsModule(function (module, exports) {
48372 var __importStar = commonjsGlobal && commonjsGlobal.__importStar || function (mod) {
48373 if (mod && mod.__esModule) return mod;
48375 if (mod != null) for (var k in mod) {
48376 if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
48378 result["default"] = mod;
48382 Object.defineProperty(exports, "__esModule", {
48386 var lineclip = __importStar(lineclip_1);
48388 * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
48389 * [lineclip](https://github.com/mapbox/lineclip).
48390 * May result in degenerate edges when clipping Polygons.
48393 * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
48394 * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
48395 * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
48397 * var bbox = [0, 0, 10, 10];
48398 * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
48400 * var clipped = turf.bboxClip(poly, bbox);
48403 * var addToMap = [bbox, poly, clipped]
48407 function bboxClip(feature, bbox) {
48408 var geom = invariant.getGeom(feature);
48409 var type = geom.type;
48410 var properties = feature.type === "Feature" ? feature.properties : {};
48411 var coords = geom.coordinates;
48415 case "MultiLineString":
48418 if (type === "LineString") {
48422 coords.forEach(function (line) {
48423 lineclip.polyline(line, bbox, lines_1);
48426 if (lines_1.length === 1) {
48427 return helpers$1.lineString(lines_1[0], properties);
48430 return helpers$1.multiLineString(lines_1, properties);
48433 return helpers$1.polygon(clipPolygon(coords, bbox), properties);
48435 case "MultiPolygon":
48436 return helpers$1.multiPolygon(coords.map(function (poly) {
48437 return clipPolygon(poly, bbox);
48441 throw new Error("geometry " + type + " not supported");
48445 exports["default"] = bboxClip;
48447 function clipPolygon(rings, bbox) {
48450 for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
48451 var ring = rings_1[_i];
48452 var clipped = lineclip.polygon(ring, bbox);
48454 if (clipped.length > 0) {
48455 if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
48456 clipped.push(clipped[0]);
48459 if (clipped.length >= 4) {
48460 outRings.push(clipped);
48468 var turf_bboxClip = /*@__PURE__*/getDefaultExportFromCjs(bboxClip_1);
48470 var fastJsonStableStringify = function fastJsonStableStringify(data, opts) {
48471 if (!opts) opts = {};
48472 if (typeof opts === 'function') opts = {
48475 var cycles = typeof opts.cycles === 'boolean' ? opts.cycles : false;
48477 var cmp = opts.cmp && function (f) {
48478 return function (node) {
48479 return function (a, b) {
48488 return f(aobj, bobj);
48494 return function stringify(node) {
48495 if (node && node.toJSON && typeof node.toJSON === 'function') {
48496 node = node.toJSON();
48499 if (node === undefined) return;
48500 if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
48501 if (_typeof(node) !== 'object') return JSON.stringify(node);
48504 if (Array.isArray(node)) {
48507 for (i = 0; i < node.length; i++) {
48509 out += stringify(node[i]) || 'null';
48515 if (node === null) return 'null';
48517 if (seen.indexOf(node) !== -1) {
48518 if (cycles) return JSON.stringify('__cycle__');
48519 throw new TypeError('Converting circular structure to JSON');
48522 var seenIndex = seen.push(node) - 1;
48523 var keys = Object.keys(node).sort(cmp && cmp(node));
48526 for (i = 0; i < keys.length; i++) {
48528 var value = stringify(node[key]);
48529 if (!value) continue;
48530 if (out) out += ',';
48531 out += JSON.stringify(key) + ':' + value;
48534 seen.splice(seenIndex, 1);
48535 return '{' + out + '}';
48539 function DEFAULT_COMPARE(a, b) {
48540 return a > b ? 1 : a < b ? -1 : 0;
48543 var SplayTree = /*#__PURE__*/function () {
48544 function SplayTree() {
48545 var compare = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_COMPARE;
48546 var noDuplicates = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
48548 _classCallCheck(this, SplayTree);
48550 this._compare = compare;
48553 this._noDuplicates = !!noDuplicates;
48556 _createClass(SplayTree, [{
48558 value: function rotateLeft(x) {
48563 if (y.left) y.left.parent = x;
48564 y.parent = x.parent;
48567 if (!x.parent) this._root = y;else if (x === x.parent.left) x.parent.left = y;else x.parent.right = y;
48572 key: "rotateRight",
48573 value: function rotateRight(x) {
48578 if (y.right) y.right.parent = x;
48579 y.parent = x.parent;
48582 if (!x.parent) this._root = y;else if (x === x.parent.left) x.parent.left = y;else x.parent.right = y;
48583 if (y) y.right = x;
48588 value: function _splay(x) {
48593 if (p.left === x) this.rotateRight(p);else this.rotateLeft(p);
48594 } else if (p.left === x && p.parent.left === p) {
48595 this.rotateRight(p.parent);
48596 this.rotateRight(p);
48597 } else if (p.right === x && p.parent.right === p) {
48598 this.rotateLeft(p.parent);
48599 this.rotateLeft(p);
48600 } else if (p.left === x && p.parent.right === p) {
48601 this.rotateRight(p);
48602 this.rotateLeft(p);
48604 this.rotateLeft(p);
48605 this.rotateRight(p);
48611 value: function splay(x) {
48612 var p, gp, ggp, l, r;
48618 if (gp && gp.parent) {
48620 if (ggp.left === gp) ggp.left = x;else ggp.right = x;
48630 if (x === p.left) {
48633 if (gp.left === p) {
48637 gp.left.parent = gp;
48638 } else gp.left = null;
48647 } else gp.right = null;
48657 } else p.left = null;
48664 if (gp.right === p) {
48668 gp.right.parent = gp;
48669 } else gp.right = null;
48678 } else gp.left = null;
48688 } else p.right = null;
48697 value: function replace(u, v) {
48698 if (!u.parent) this._root = v;else if (u === u.parent.left) u.parent.left = v;else u.parent.right = v;
48699 if (v) v.parent = u.parent;
48703 value: function minNode() {
48704 var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
48705 if (u) while (u.left) {
48712 value: function maxNode() {
48713 var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
48714 if (u) while (u.right) {
48721 value: function insert(key, data) {
48722 var z = this._root;
48724 var comp = this._compare;
48727 if (this._noDuplicates) {
48730 cmp = comp(z.key, key);
48731 if (cmp === 0) return;else if (comp(z.key, key) < 0) z = z.right;else z = z.left;
48736 if (comp(z.key, key) < 0) z = z.right;else z = z.left;
48747 if (!p) this._root = z;else if (comp(p.key, z.key) < 0) p.right = z;else p.left = z;
48754 value: function find(key) {
48755 var z = this._root;
48756 var comp = this._compare;
48759 var cmp = comp(z.key, key);
48760 if (cmp < 0) z = z.right;else if (cmp > 0) z = z.left;else return z;
48766 * Whether the tree contains a node with the given key
48768 * @return {boolean} true/false
48773 value: function contains(key) {
48774 var node = this._root;
48775 var comparator = this._compare;
48778 var cmp = comparator(key, node.key);
48779 if (cmp === 0) return true;else if (cmp < 0) node = node.left;else node = node.right;
48786 value: function remove(key) {
48787 var z = this.find(key);
48788 if (!z) return false;
48790 if (!z.left) this.replace(z, z.right);else if (!z.right) this.replace(z, z.left);else {
48791 var y = this.minNode(z.right);
48793 if (y.parent !== z) {
48794 this.replace(y, y.right);
48796 y.right.parent = y;
48799 this.replace(z, y);
48808 value: function removeNode(z) {
48809 if (!z) return false;
48811 if (!z.left) this.replace(z, z.right);else if (!z.right) this.replace(z, z.left);else {
48812 var y = this.minNode(z.right);
48814 if (y.parent !== z) {
48815 this.replace(y, y.right);
48817 y.right.parent = y;
48820 this.replace(z, y);
48829 value: function erase(key) {
48830 var z = this.find(key);
48839 sMax = this.maxNode(s);
48845 if (s) sMax.right = t;else this._root = t;
48852 * Removes and returns the node with smallest key
48858 value: function pop() {
48859 var node = this._root,
48860 returnValue = null;
48863 while (node.left) {
48871 this.remove(node.key);
48874 return returnValue;
48876 /* eslint-disable class-methods-use-this */
48880 * @param {Node} node
48886 value: function next(node) {
48887 var successor = node;
48890 if (successor.right) {
48891 successor = successor.right;
48893 while (successor && successor.left) {
48894 successor = successor.left;
48897 successor = node.parent;
48899 while (successor && successor.right === node) {
48901 successor = successor.parent;
48910 * @param {Node} node
48916 value: function prev(node) {
48917 var predecessor = node;
48920 if (predecessor.left) {
48921 predecessor = predecessor.left;
48923 while (predecessor && predecessor.right) {
48924 predecessor = predecessor.right;
48927 predecessor = node.parent;
48929 while (predecessor && predecessor.left === node) {
48930 node = predecessor;
48931 predecessor = predecessor.parent;
48936 return predecessor;
48938 /* eslint-enable class-methods-use-this */
48941 * @param {forEachCallback} callback
48942 * @return {SplayTree}
48947 value: function forEach(callback) {
48948 var current = this._root;
48954 // Reach the left most Node of the current Node
48956 // Place pointer to a tree node on the stack
48957 // before traversing the node's left subtree
48959 current = current.left;
48961 // BackTrack from the empty subtree and visit the Node
48962 // at the top of the stack; however, if the stack is
48963 // empty you are done
48964 if (s.length > 0) {
48966 callback(current, i++); // We have visited the node and its left
48967 // subtree. Now, it's right subtree's turn
48969 current = current.right;
48970 } else done = true;
48977 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
48979 * @param {Key} high
48980 * @param {Function} fn
48982 * @return {SplayTree}
48987 value: function range(low, high, fn, ctx) {
48989 var compare = this._compare;
48990 var node = this._root,
48993 while (Q.length !== 0 || node) {
48999 cmp = compare(node.key, high);
49003 } else if (compare(node.key, low) >= 0) {
49004 if (fn.call(ctx, node)) return this; // stop if smth is returned
49014 * Returns all keys in order
49015 * @return {Array<Key>}
49020 value: function keys() {
49021 var current = this._root;
49029 current = current.left;
49031 if (s.length > 0) {
49033 r.push(current.key);
49034 current = current.right;
49035 } else done = true;
49042 * Returns `data` fields of all nodes in order.
49043 * @return {Array<Value>}
49048 value: function values() {
49049 var current = this._root;
49057 current = current.left;
49059 if (s.length > 0) {
49061 r.push(current.data);
49062 current = current.right;
49063 } else done = true;
49070 * Returns node at given index
49071 * @param {number} index
49077 value: function at(index) {
49078 // removed after a consideration, more misleading than useful
49079 // index = index % this.size;
49080 // if (index < 0) index = this.size - index;
49081 var current = this._root;
49089 current = current.left;
49091 if (s.length > 0) {
49093 if (i === index) return current;
49095 current = current.right;
49096 } else done = true;
49103 * Bulk-load items. Both array have to be same size
49104 * @param {Array<Key>} keys
49105 * @param {Array<Value>} [values]
49106 * @param {Boolean} [presort=false] Pre-sort keys and values, using
49107 * tree's comparator. Sorting is done
49109 * @return {AVLTree}
49114 value: function load() {
49115 var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
49116 var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
49117 var presort = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
49118 if (this._size !== 0) throw new Error('bulk-load: tree is not empty');
49119 var size = keys.length;
49120 if (presort) sort(keys, values, 0, size - 1, this._compare);
49121 this._root = loadRecursive(null, keys, values, 0, size);
49127 value: function min() {
49128 var node = this.minNode(this._root);
49129 if (node) return node.key;else return null;
49133 value: function max() {
49134 var node = this.maxNode(this._root);
49135 if (node) return node.key;else return null;
49139 value: function isEmpty() {
49140 return this._root === null;
49144 get: function get() {
49148 * Create a tree and load it with items
49149 * @param {Array<Key>} keys
49150 * @param {Array<Value>?} [values]
49151 * @param {Function?} [comparator]
49152 * @param {Boolean?} [presort=false] Pre-sort keys and values, using
49153 * tree's comparator. Sorting is done
49155 * @param {Boolean?} [noDuplicates=false] Allow duplicates
49156 * @return {SplayTree}
49161 value: function createTree(keys, values, comparator, presort, noDuplicates) {
49162 return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
49169 function loadRecursive(parent, keys, values, start, end) {
49170 var size = end - start;
49173 var middle = start + Math.floor(size / 2);
49174 var key = keys[middle];
49175 var data = values[middle];
49181 node.left = loadRecursive(node, keys, values, start, middle);
49182 node.right = loadRecursive(node, keys, values, middle + 1, end);
49189 function sort(keys, values, left, right, compare) {
49190 if (left >= right) return;
49191 var pivot = keys[left + right >> 1];
49198 } while (compare(keys[i], pivot) < 0);
49202 } while (compare(keys[j], pivot) > 0);
49209 values[i] = values[j];
49213 sort(keys, values, left, j, compare);
49214 sort(keys, values, j + 1, right, compare);
49218 var NON_CONTRIBUTING = 1;
49219 var SAME_TRANSITION = 2;
49220 var DIFFERENT_TRANSITION = 3;
49222 var INTERSECTION = 0;
49224 var DIFFERENCE = 2;
49228 * @param {SweepEvent} event
49229 * @param {SweepEvent} prev
49230 * @param {Operation} operation
49233 function computeFields(event, prev, operation) {
49234 // compute inOut and otherInOut fields
49235 if (prev === null) {
49236 event.inOut = false;
49237 event.otherInOut = true; // previous line segment in sweepline belongs to the same polygon
49239 if (event.isSubject === prev.isSubject) {
49240 event.inOut = !prev.inOut;
49241 event.otherInOut = prev.otherInOut; // previous line segment in sweepline belongs to the clipping polygon
49243 event.inOut = !prev.otherInOut;
49244 event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
49245 } // compute prevInResult field
49249 event.prevInResult = !inResult(prev, operation) || prev.isVertical() ? prev.prevInResult : prev;
49251 } // check if the line segment belongs to the Boolean operation
49254 var isInResult = inResult(event, operation);
49257 event.resultTransition = determineResultTransition(event, operation);
49259 event.resultTransition = 0;
49262 /* eslint-disable indent */
49264 function inResult(event, operation) {
49265 switch (event.type) {
49267 switch (operation) {
49269 return !event.otherInOut;
49272 return event.otherInOut;
49275 // return (event.isSubject && !event.otherInOut) ||
49276 // (!event.isSubject && event.otherInOut);
49277 return event.isSubject && event.otherInOut || !event.isSubject && !event.otherInOut;
49285 case SAME_TRANSITION:
49286 return operation === INTERSECTION || operation === UNION;
49288 case DIFFERENT_TRANSITION:
49289 return operation === DIFFERENCE;
49291 case NON_CONTRIBUTING:
49297 /* eslint-enable indent */
49300 function determineResultTransition(event, operation) {
49301 var thisIn = !event.inOut;
49302 var thatIn = !event.otherInOut;
49305 switch (operation) {
49307 isIn = thisIn && thatIn;
49311 isIn = thisIn || thatIn;
49315 isIn = thisIn ^ thatIn;
49319 if (event.isSubject) {
49320 isIn = thisIn && !thatIn;
49322 isIn = thatIn && !thisIn;
49328 return isIn ? +1 : -1;
49331 var SweepEvent = /*#__PURE__*/function () {
49335 * @class {SweepEvent}
49336 * @param {Array.<Number>} point
49337 * @param {Boolean} left
49338 * @param {SweepEvent=} otherEvent
49339 * @param {Boolean} isSubject
49340 * @param {Number} edgeType
49342 function SweepEvent(point, left, otherEvent, isSubject, edgeType) {
49343 _classCallCheck(this, SweepEvent);
49346 * Is left endpoint?
49351 * @type {Array.<Number>}
49354 this.point = point;
49356 * Other edge reference
49357 * @type {SweepEvent}
49360 this.otherEvent = otherEvent;
49362 * Belongs to source or clipping polygon
49366 this.isSubject = isSubject;
49368 * Edge contribution type
49372 this.type = edgeType || NORMAL;
49374 * In-out transition for the sweepline crossing polygon
49378 this.inOut = false;
49383 this.otherInOut = false;
49385 * Previous event in result?
49386 * @type {SweepEvent}
49389 this.prevInResult = null;
49391 * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
49395 this.resultTransition = 0; // connection step
49401 this.otherPos = -1;
49406 this.outputContourId = -1;
49407 this.isExteriorRing = true; // TODO: Looks unused, remove?
49410 * @param {Array.<Number>} p
49411 * @return {Boolean}
49415 _createClass(SweepEvent, [{
49417 value: function isBelow(p) {
49418 var p0 = this.point,
49419 p1 = this.otherEvent.point;
49420 return this.left ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0 // signedArea(this.point, this.otherEvent.point, p) > 0 :
49421 : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0; //signedArea(this.otherEvent.point, this.point, p) > 0;
49424 * @param {Array.<Number>} p
49425 * @return {Boolean}
49430 value: function isAbove(p) {
49431 return !this.isBelow(p);
49434 * @return {Boolean}
49439 value: function isVertical() {
49440 return this.point[0] === this.otherEvent.point[0];
49443 * Does event belong to result?
49444 * @return {Boolean}
49449 value: function clone() {
49450 var copy = new SweepEvent(this.point, this.left, this.otherEvent, this.isSubject, this.type);
49451 copy.contourId = this.contourId;
49452 copy.resultTransition = this.resultTransition;
49453 copy.prevInResult = this.prevInResult;
49454 copy.isExteriorRing = this.isExteriorRing;
49455 copy.inOut = this.inOut;
49456 copy.otherInOut = this.otherInOut;
49461 get: function get() {
49462 return this.resultTransition !== 0;
49469 function equals(p1, p2) {
49470 if (p1[0] === p2[0]) {
49471 if (p1[1] === p2[1]) {
49479 } // const EPSILON = 1e-9;
49480 // const abs = Math.abs;
49481 // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
49482 // Precision problem.
49484 // module.exports = function equals(p1, p2) {
49485 // return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
49488 var epsilon$1 = 1.1102230246251565e-16;
49489 var splitter = 134217729;
49490 var resulterrbound = (3 + 8 * epsilon$1) * epsilon$1; // fast_expansion_sum_zeroelim routine from oritinal code
49492 function sum(elen, e, flen, f, h) {
49493 var Q, Qnew, hh, bvirt;
49499 if (fnow > enow === fnow > -enow) {
49501 enow = e[++eindex];
49504 fnow = f[++findex];
49509 if (eindex < elen && findex < flen) {
49510 if (fnow > enow === fnow > -enow) {
49512 hh = Q - (Qnew - enow);
49513 enow = e[++eindex];
49516 hh = Q - (Qnew - fnow);
49517 fnow = f[++findex];
49526 while (eindex < elen && findex < flen) {
49527 if (fnow > enow === fnow > -enow) {
49530 hh = Q - (Qnew - bvirt) + (enow - bvirt);
49531 enow = e[++eindex];
49535 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
49536 fnow = f[++findex];
49547 while (eindex < elen) {
49550 hh = Q - (Qnew - bvirt) + (enow - bvirt);
49551 enow = e[++eindex];
49559 while (findex < flen) {
49562 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
49563 fnow = f[++findex];
49571 if (Q !== 0 || hindex === 0) {
49577 function estimate(elen, e) {
49580 for (var i = 1; i < elen; i++) {
49587 return new Float64Array(n);
49590 var ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
49591 var ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
49592 var ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
49599 function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
49600 var acxtail, acytail, bcxtail, bcytail;
49602 var bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
49609 c = splitter * acx;
49610 ahi = c - (c - acx);
49612 c = splitter * bcy;
49613 bhi = c - (c - bcy);
49615 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49617 c = splitter * acy;
49618 ahi = c - (c - acy);
49620 c = splitter * bcx;
49621 bhi = c - (c - bcx);
49623 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49626 B[0] = s0 - (_i + bvirt) + (bvirt - t0);
49629 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49632 B[1] = _0 - (_i + bvirt) + (bvirt - t1);
49635 B[2] = _j - (u3 - bvirt) + (_i - bvirt);
49637 var det = estimate(4, B);
49638 var errbound = ccwerrboundB * detsum;
49640 if (det >= errbound || -det >= errbound) {
49645 acxtail = ax - (acx + bvirt) + (bvirt - cx);
49647 bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
49649 acytail = ay - (acy + bvirt) + (bvirt - cy);
49651 bcytail = by - (bcy + bvirt) + (bvirt - cy);
49653 if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
49657 errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
49658 det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail);
49659 if (det >= errbound || -det >= errbound) return det;
49660 s1 = acxtail * bcy;
49661 c = splitter * acxtail;
49662 ahi = c - (c - acxtail);
49663 alo = acxtail - ahi;
49664 c = splitter * bcy;
49665 bhi = c - (c - bcy);
49667 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49668 t1 = acytail * bcx;
49669 c = splitter * acytail;
49670 ahi = c - (c - acytail);
49671 alo = acytail - ahi;
49672 c = splitter * bcx;
49673 bhi = c - (c - bcx);
49675 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49678 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49681 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49684 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49687 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49689 var C1len = sum(4, B, 4, u, C1);
49690 s1 = acx * bcytail;
49691 c = splitter * acx;
49692 ahi = c - (c - acx);
49694 c = splitter * bcytail;
49695 bhi = c - (c - bcytail);
49696 blo = bcytail - bhi;
49697 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49698 t1 = acy * bcxtail;
49699 c = splitter * acy;
49700 ahi = c - (c - acy);
49702 c = splitter * bcxtail;
49703 bhi = c - (c - bcxtail);
49704 blo = bcxtail - bhi;
49705 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49708 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49711 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49714 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49717 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49719 var C2len = sum(C1len, C1, 4, u, C2);
49720 s1 = acxtail * bcytail;
49721 c = splitter * acxtail;
49722 ahi = c - (c - acxtail);
49723 alo = acxtail - ahi;
49724 c = splitter * bcytail;
49725 bhi = c - (c - bcytail);
49726 blo = bcytail - bhi;
49727 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49728 t1 = acytail * bcxtail;
49729 c = splitter * acytail;
49730 ahi = c - (c - acytail);
49731 alo = acytail - ahi;
49732 c = splitter * bcxtail;
49733 bhi = c - (c - bcxtail);
49734 blo = bcxtail - bhi;
49735 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49738 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49741 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49744 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49747 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49749 var Dlen = sum(C2len, C2, 4, u, D);
49750 return D[Dlen - 1];
49753 function orient2d(ax, ay, bx, by, cx, cy) {
49754 var detleft = (ay - cy) * (bx - cx);
49755 var detright = (ax - cx) * (by - cy);
49756 var det = detleft - detright;
49757 if (detleft === 0 || detright === 0 || detleft > 0 !== detright > 0) return det;
49758 var detsum = Math.abs(detleft + detright);
49759 if (Math.abs(det) >= ccwerrboundA * detsum) return det;
49760 return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
49764 * Signed area of the triangle (p0, p1, p2)
49765 * @param {Array.<Number>} p0
49766 * @param {Array.<Number>} p1
49767 * @param {Array.<Number>} p2
49771 function signedArea(p0, p1, p2) {
49772 var res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
49773 if (res > 0) return -1;
49774 if (res < 0) return 1;
49779 * @param {SweepEvent} e1
49780 * @param {SweepEvent} e2
49784 function compareEvents(e1, e2) {
49786 var p2 = e2.point; // Different x-coordinate
49788 if (p1[0] > p2[0]) return 1;
49789 if (p1[0] < p2[0]) return -1; // Different points, but same x-coordinate
49790 // Event with lower y-coordinate is processed first
49792 if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1;
49793 return specialCases(e1, e2, p1);
49795 /* eslint-disable no-unused-vars */
49797 function specialCases(e1, e2, p1, p2) {
49798 // Same coordinates, but one is a left endpoint and the other is
49799 // a right endpoint. The right endpoint is processed first
49800 if (e1.left !== e2.left) return e1.left ? 1 : -1; // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
49801 // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
49802 // Same coordinates, both events
49803 // are left endpoints or right endpoints.
49806 if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
49807 // the event associate to the bottom segment is processed first
49808 return !e1.isBelow(e2.otherEvent.point) ? 1 : -1;
49811 return !e1.isSubject && e2.isSubject ? 1 : -1;
49813 /* eslint-enable no-unused-vars */
49816 * @param {SweepEvent} se
49817 * @param {Array.<Number>} p
49818 * @param {Queue} queue
49822 function divideSegment(se, p, queue) {
49823 var r = new SweepEvent(p, false, se, se.isSubject);
49824 var l = new SweepEvent(p, true, se.otherEvent, se.isSubject);
49825 /* eslint-disable no-console */
49827 if (equals(se.point, se.otherEvent.point)) {
49828 console.warn('what is that, a collapsed segment?', se);
49830 /* eslint-enable no-console */
49833 r.contourId = l.contourId = se.contourId; // avoid a rounding error. The left event would be processed after the right event
49835 if (compareEvents(l, se.otherEvent) > 0) {
49836 se.otherEvent.left = true;
49838 } // avoid a rounding error. The left event would be processed after the right event
49839 // if (compareEvents(se, r) > 0) {}
49842 se.otherEvent.otherEvent = l;
49849 //const EPS = 1e-9;
49852 * Finds the magnitude of the cross product of two vectors (if we pretend
49853 * they're in three dimensions)
49855 * @param {Object} a First vector
49856 * @param {Object} b Second vector
49858 * @returns {Number} The magnitude of the cross product
49860 function crossProduct(a, b) {
49861 return a[0] * b[1] - a[1] * b[0];
49864 * Finds the dot product of two vectors.
49866 * @param {Object} a First vector
49867 * @param {Object} b Second vector
49869 * @returns {Number} The dot product
49873 function dotProduct(a, b) {
49874 return a[0] * b[0] + a[1] * b[1];
49877 * Finds the intersection (if any) between two line segments a and b, given the
49878 * line segments' end points a1, a2 and b1, b2.
49880 * This algorithm is based on Schneider and Eberly.
49881 * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
49884 * @param {Array.<Number>} a1 point of first line
49885 * @param {Array.<Number>} a2 point of first line
49886 * @param {Array.<Number>} b1 point of second line
49887 * @param {Array.<Number>} b2 point of second line
49888 * @param {Boolean=} noEndpointTouch whether to skip single touchpoints
49889 * (meaning connected segments) as
49891 * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
49892 * intersection. If they overlap, the two end points of the overlapping segment.
49897 function intersection (a1, a2, b1, b2, noEndpointTouch) {
49898 // The algorithm expects our lines in the form P + sd, where P is a point,
49899 // s is on the interval [0, 1], and d is a vector.
49900 // We are passed two points. P can be the first point of each pair. The
49901 // vector, then, could be thought of as the distance (in x and y components)
49902 // from the first point to the second point.
49903 // So first, let's make our vectors:
49904 var va = [a2[0] - a1[0], a2[1] - a1[1]];
49905 var vb = [b2[0] - b1[0], b2[1] - b1[1]]; // We also define a function to convert back to regular point form:
49907 /* eslint-disable arrow-body-style */
49909 function toPoint(p, s, d) {
49910 return [p[0] + s * d[0], p[1] + s * d[1]];
49912 /* eslint-enable arrow-body-style */
49913 // The rest is pretty much a straight port of the algorithm.
49916 var e = [b1[0] - a1[0], b1[1] - a1[1]];
49917 var kross = crossProduct(va, vb);
49918 var sqrKross = kross * kross;
49919 var sqrLenA = dotProduct(va, va); //const sqrLenB = dotProduct(vb, vb);
49920 // Check for line intersection. This works because of the properties of the
49921 // cross product -- specifically, two vectors are parallel if and only if the
49922 // cross product is the 0 vector. The full calculation involves relative error
49923 // to account for possible very small line segments. See Schneider & Eberly
49927 /* EPS * sqrLenB * sqLenA */
49929 // If they're not parallel, then (because these are line segments) they
49930 // still might not actually intersect. This code checks that the
49931 // intersection point of the lines is actually on both line segments.
49932 var s = crossProduct(e, vb) / kross;
49934 if (s < 0 || s > 1) {
49935 // not on line segment a
49939 var t = crossProduct(e, va) / kross;
49941 if (t < 0 || t > 1) {
49942 // not on line segment b
49946 if (s === 0 || s === 1) {
49947 // on an endpoint of line segment a
49948 return noEndpointTouch ? null : [toPoint(a1, s, va)];
49951 if (t === 0 || t === 1) {
49952 // on an endpoint of line segment b
49953 return noEndpointTouch ? null : [toPoint(b1, t, vb)];
49956 return [toPoint(a1, s, va)];
49957 } // If we've reached this point, then the lines are either parallel or the
49958 // same, but the segments could overlap partially or fully, or not at all.
49959 // So we need to find the overlap, if any. To do that, we can use e, which is
49960 // the (vector) difference between the two initial points. If this is parallel
49961 // with the line itself, then the two lines are the same line, and there will
49963 //const sqrLenE = dotProduct(e, e);
49966 kross = crossProduct(e, va);
49967 sqrKross = kross * kross;
49970 /* EPS * sqLenB * sqLenE */
49972 // Lines are just parallel, not the same. No overlap.
49976 var sa = dotProduct(va, e) / sqrLenA;
49977 var sb = sa + dotProduct(va, vb) / sqrLenA;
49978 var smin = Math.min(sa, sb);
49979 var smax = Math.max(sa, sb); // this is, essentially, the FindIntersection acting on floats from
49980 // Schneider & Eberly, just inlined into this function.
49982 if (smin <= 1 && smax >= 0) {
49983 // overlap on an end point
49985 return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
49989 return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
49992 if (noEndpointTouch && smin === 0 && smax === 1) return null; // There's overlap on a segment -- two points of intersection. Return both.
49994 return [toPoint(a1, smin > 0 ? smin : 0, va), toPoint(a1, smax < 1 ? smax : 1, va)];
50001 * @param {SweepEvent} se1
50002 * @param {SweepEvent} se2
50003 * @param {Queue} queue
50007 function possibleIntersection(se1, se2, queue) {
50008 // that disallows self-intersecting polygons,
50009 // did cost us half a day, so I'll leave it
50011 // if (se1.isSubject === se2.isSubject) return;
50012 var inter = intersection(se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
50013 var nintersections = inter ? inter.length : 0;
50014 if (nintersections === 0) return 0; // no intersection
50015 // the line segments intersect at an endpoint of both line segments
50017 if (nintersections === 1 && (equals(se1.point, se2.point) || equals(se1.otherEvent.point, se2.otherEvent.point))) {
50021 if (nintersections === 2 && se1.isSubject === se2.isSubject) {
50022 // if(se1.contourId === se2.contourId){
50023 // console.warn('Edges of the same polygon overlap',
50024 // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
50026 //throw new Error('Edges of the same polygon overlap');
50028 } // The line segments associated to se1 and se2 intersect
50031 if (nintersections === 1) {
50032 // if the intersection point is not an endpoint of se1
50033 if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
50034 divideSegment(se1, inter[0], queue);
50035 } // if the intersection point is not an endpoint of se2
50038 if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
50039 divideSegment(se2, inter[0], queue);
50043 } // The line segments associated to se1 and se2 overlap
50047 var leftCoincide = false;
50048 var rightCoincide = false;
50050 if (equals(se1.point, se2.point)) {
50051 leftCoincide = true; // linked
50052 } else if (compareEvents(se1, se2) === 1) {
50053 events.push(se2, se1);
50055 events.push(se1, se2);
50058 if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
50059 rightCoincide = true;
50060 } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
50061 events.push(se2.otherEvent, se1.otherEvent);
50063 events.push(se1.otherEvent, se2.otherEvent);
50066 if (leftCoincide && rightCoincide || leftCoincide) {
50067 // both line segments are equal or share the left endpoint
50068 se2.type = NON_CONTRIBUTING;
50069 se1.type = se2.inOut === se1.inOut ? SAME_TRANSITION : DIFFERENT_TRANSITION;
50071 if (leftCoincide && !rightCoincide) {
50072 // honestly no idea, but changing events selection from [2, 1]
50073 // to [0, 1] fixes the overlapping self-intersecting polygons issue
50074 divideSegment(events[1].otherEvent, events[0].point, queue);
50078 } // the line segments share the right endpoint
50081 if (rightCoincide) {
50082 divideSegment(events[0], events[1].point, queue);
50084 } // no line segment includes totally the other one
50087 if (events[0] !== events[3].otherEvent) {
50088 divideSegment(events[0], events[1].point, queue);
50089 divideSegment(events[1], events[2].point, queue);
50091 } // one line segment includes the other one
50094 divideSegment(events[0], events[1].point, queue);
50095 divideSegment(events[3].otherEvent, events[2].point, queue);
50100 * @param {SweepEvent} le1
50101 * @param {SweepEvent} le2
50105 function compareSegments(le1, le2) {
50106 if (le1 === le2) return 0; // Segments are not collinear
50108 if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 || signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
50109 // If they share their left endpoint use the right endpoint to sort
50110 if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1; // Different left endpoint: use the left endpoint to sort
50112 if (le1.point[0] === le2.point[0]) return le1.point[1] < le2.point[1] ? -1 : 1; // has the line segment associated to e1 been inserted
50113 // into S after the line segment associated to e2 ?
50115 if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1; // The line segment associated to e2 has been inserted
50116 // into S after the line segment associated to e1
50118 return le1.isBelow(le2.point) ? -1 : 1;
50121 if (le1.isSubject === le2.isSubject) {
50123 var p1 = le1.point,
50126 if (p1[0] === p2[0] && p1[1] === p2[1]
50127 /*equals(le1.point, le2.point)*/
50129 p1 = le1.otherEvent.point;
50130 p2 = le2.otherEvent.point;
50131 if (p1[0] === p2[0] && p1[1] === p2[1]) return 0;else return le1.contourId > le2.contourId ? 1 : -1;
50134 // Segments are collinear, but belong to separate polygons
50135 return le1.isSubject ? -1 : 1;
50138 return compareEvents(le1, le2) === 1 ? 1 : -1;
50141 function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
50142 var sweepLine = new SplayTree(compareSegments);
50143 var sortedEvents = [];
50144 var rightbound = Math.min(sbbox[2], cbbox[2]);
50145 var prev, next, begin;
50147 while (eventQueue.length !== 0) {
50148 var event = eventQueue.pop();
50149 sortedEvents.push(event); // optimization by bboxes for intersection and difference goes here
50151 if (operation === INTERSECTION && event.point[0] > rightbound || operation === DIFFERENCE && event.point[0] > sbbox[2]) {
50156 next = prev = sweepLine.insert(event);
50157 begin = sweepLine.minNode();
50158 if (prev !== begin) prev = sweepLine.prev(prev);else prev = null;
50159 next = sweepLine.next(next);
50160 var prevEvent = prev ? prev.key : null;
50161 var prevprevEvent = void 0;
50162 computeFields(event, prevEvent, operation);
50165 if (possibleIntersection(event, next.key, eventQueue) === 2) {
50166 computeFields(event, prevEvent, operation);
50167 computeFields(event, next.key, operation);
50172 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
50173 var prevprev = prev;
50174 if (prevprev !== begin) prevprev = sweepLine.prev(prevprev);else prevprev = null;
50175 prevprevEvent = prevprev ? prevprev.key : null;
50176 computeFields(prevEvent, prevprevEvent, operation);
50177 computeFields(event, prevEvent, operation);
50181 event = event.otherEvent;
50182 next = prev = sweepLine.find(event);
50184 if (prev && next) {
50185 if (prev !== begin) prev = sweepLine.prev(prev);else prev = null;
50186 next = sweepLine.next(next);
50187 sweepLine.remove(event);
50189 if (next && prev) {
50190 possibleIntersection(prev.key, next.key, eventQueue);
50196 return sortedEvents;
50199 var Contour = /*#__PURE__*/function () {
50205 function Contour() {
50206 _classCallCheck(this, Contour);
50210 this.holeOf = null;
50214 _createClass(Contour, [{
50216 value: function isExterior() {
50217 return this.holeOf == null;
50225 * @param {Array.<SweepEvent>} sortedEvents
50226 * @return {Array.<SweepEvent>}
50229 function orderEvents(sortedEvents) {
50230 var event, i, len, tmp;
50231 var resultEvents = [];
50233 for (i = 0, len = sortedEvents.length; i < len; i++) {
50234 event = sortedEvents[i];
50236 if (event.left && event.inResult || !event.left && event.otherEvent.inResult) {
50237 resultEvents.push(event);
50239 } // Due to overlapping edges the resultEvents array can be not wholly sorted
50242 var sorted = false;
50247 for (i = 0, len = resultEvents.length; i < len; i++) {
50248 if (i + 1 < len && compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
50249 tmp = resultEvents[i];
50250 resultEvents[i] = resultEvents[i + 1];
50251 resultEvents[i + 1] = tmp;
50257 for (i = 0, len = resultEvents.length; i < len; i++) {
50258 event = resultEvents[i];
50259 event.otherPos = i;
50260 } // imagine, the right event is found in the beginning of the queue,
50261 // when his left counterpart is not marked yet
50264 for (i = 0, len = resultEvents.length; i < len; i++) {
50265 event = resultEvents[i];
50268 tmp = event.otherPos;
50269 event.otherPos = event.otherEvent.otherPos;
50270 event.otherEvent.otherPos = tmp;
50274 return resultEvents;
50277 * @param {Number} pos
50278 * @param {Array.<SweepEvent>} resultEvents
50279 * @param {Object>} processed
50284 function nextPos(pos, resultEvents, processed, origPos) {
50285 var newPos = pos + 1,
50286 p = resultEvents[pos].point,
50288 var length = resultEvents.length;
50289 if (newPos < length) p1 = resultEvents[newPos].point;
50291 while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
50292 if (!processed[newPos]) {
50298 p1 = resultEvents[newPos].point;
50303 while (processed[newPos] && newPos > origPos) {
50310 function initializeContourFromContext(event, contours, contourId) {
50311 var contour = new Contour();
50313 if (event.prevInResult != null) {
50314 var prevInResult = event.prevInResult; // Note that it is valid to query the "previous in result" for its output contour id,
50315 // because we must have already processed it (i.e., assigned an output contour id)
50316 // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
50319 var lowerContourId = prevInResult.outputContourId;
50320 var lowerResultTransition = prevInResult.resultTransition;
50322 if (lowerResultTransition > 0) {
50323 // We are inside. Now we have to check if the thing below us is another hole or
50324 // an exterior contour.
50325 var lowerContour = contours[lowerContourId];
50327 if (lowerContour.holeOf != null) {
50328 // The lower contour is a hole => Connect the new contour as a hole to its parent,
50329 // and use same depth.
50330 var parentContourId = lowerContour.holeOf;
50331 contours[parentContourId].holeIds.push(contourId);
50332 contour.holeOf = parentContourId;
50333 contour.depth = contours[lowerContourId].depth;
50335 // The lower contour is an exterior contour => Connect the new contour as a hole,
50336 // and increment depth.
50337 contours[lowerContourId].holeIds.push(contourId);
50338 contour.holeOf = lowerContourId;
50339 contour.depth = contours[lowerContourId].depth + 1;
50342 // We are outside => this contour is an exterior contour of same depth.
50343 contour.holeOf = null;
50344 contour.depth = contours[lowerContourId].depth;
50347 // There is no lower/previous contour => this contour is an exterior contour of depth 0.
50348 contour.holeOf = null;
50355 * @param {Array.<SweepEvent>} sortedEvents
50356 * @return {Array.<*>} polygons
50360 function connectEdges(sortedEvents) {
50362 var resultEvents = orderEvents(sortedEvents); // "false"-filled array
50364 var processed = {};
50367 var _loop = function _loop() {
50368 if (processed[i]) {
50372 var contourId = contours.length;
50373 var contour = initializeContourFromContext(resultEvents[i], contours, contourId); // Helper function that combines marking an event as processed with assigning its output contour ID
50375 var markAsProcessed = function markAsProcessed(pos) {
50376 processed[pos] = true;
50377 resultEvents[pos].outputContourId = contourId;
50382 var initial = resultEvents[i].point;
50383 contour.points.push(initial);
50384 /* eslint no-constant-condition: "off" */
50387 markAsProcessed(pos);
50388 pos = resultEvents[pos].otherPos;
50389 markAsProcessed(pos);
50390 contour.points.push(resultEvents[pos].point);
50391 pos = nextPos(pos, resultEvents, processed, origPos);
50393 if (pos == origPos) {
50398 contours.push(contour);
50401 for (i = 0, len = resultEvents.length; i < len; i++) {
50402 var _ret = _loop();
50404 if (_ret === "continue") continue;
50410 var tinyqueue = TinyQueue;
50411 var _default$1 = TinyQueue;
50413 function TinyQueue(data, compare) {
50414 if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare);
50415 this.data = data || [];
50416 this.length = this.data.length;
50417 this.compare = compare || defaultCompare$1;
50419 if (this.length > 0) {
50420 for (var i = (this.length >> 1) - 1; i >= 0; i--) {
50426 function defaultCompare$1(a, b) {
50427 return a < b ? -1 : a > b ? 1 : 0;
50430 TinyQueue.prototype = {
50431 push: function push(item) {
50432 this.data.push(item);
50435 this._up(this.length - 1);
50437 pop: function pop() {
50438 if (this.length === 0) return undefined;
50439 var top = this.data[0];
50442 if (this.length > 0) {
50443 this.data[0] = this.data[this.length];
50451 peek: function peek() {
50452 return this.data[0];
50454 _up: function _up(pos) {
50455 var data = this.data;
50456 var compare = this.compare;
50457 var item = data[pos];
50460 var parent = pos - 1 >> 1;
50461 var current = data[parent];
50462 if (compare(item, current) >= 0) break;
50463 data[pos] = current;
50469 _down: function _down(pos) {
50470 var data = this.data;
50471 var compare = this.compare;
50472 var halfLength = this.length >> 1;
50473 var item = data[pos];
50475 while (pos < halfLength) {
50476 var left = (pos << 1) + 1;
50477 var right = left + 1;
50478 var best = data[left];
50480 if (right < this.length && compare(data[right], best) < 0) {
50482 best = data[right];
50485 if (compare(best, item) >= 0) break;
50493 tinyqueue["default"] = _default$1;
50495 var max$5 = Math.max;
50496 var min$a = Math.min;
50499 function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
50500 var i, len, s1, s2, e1, e2;
50502 for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
50503 s1 = contourOrHole[i];
50504 s2 = contourOrHole[i + 1];
50505 e1 = new SweepEvent(s1, false, undefined, isSubject);
50506 e2 = new SweepEvent(s2, false, e1, isSubject);
50507 e1.otherEvent = e2;
50509 if (s1[0] === s2[0] && s1[1] === s2[1]) {
50510 continue; // skip collapsed edges, or it breaks
50513 e1.contourId = e2.contourId = depth;
50515 if (!isExteriorRing) {
50516 e1.isExteriorRing = false;
50517 e2.isExteriorRing = false;
50520 if (compareEvents(e1, e2) > 0) {
50528 bbox[0] = min$a(bbox[0], x);
50529 bbox[1] = min$a(bbox[1], y);
50530 bbox[2] = max$5(bbox[2], x);
50531 bbox[3] = max$5(bbox[3], y); // Pushing it so the queue is sorted from left to right,
50532 // with object on the left having the highest priority.
50539 function fillQueue(subject, clipping, sbbox, cbbox, operation) {
50540 var eventQueue = new tinyqueue(null, compareEvents);
50541 var polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
50543 for (i = 0, ii = subject.length; i < ii; i++) {
50544 polygonSet = subject[i];
50546 for (j = 0, jj = polygonSet.length; j < jj; j++) {
50547 isExteriorRing = j === 0;
50548 if (isExteriorRing) contourId++;
50549 processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
50553 for (i = 0, ii = clipping.length; i < ii; i++) {
50554 polygonSet = clipping[i];
50556 for (j = 0, jj = polygonSet.length; j < jj; j++) {
50557 isExteriorRing = j === 0;
50558 if (operation === DIFFERENCE) isExteriorRing = false;
50559 if (isExteriorRing) contourId++;
50560 processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
50569 function trivialOperation(subject, clipping, operation) {
50572 if (subject.length * clipping.length === 0) {
50573 if (operation === INTERSECTION) {
50575 } else if (operation === DIFFERENCE) {
50577 } else if (operation === UNION || operation === XOR) {
50578 result = subject.length === 0 ? clipping : subject;
50585 function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
50588 if (sbbox[0] > cbbox[2] || cbbox[0] > sbbox[2] || sbbox[1] > cbbox[3] || cbbox[1] > sbbox[3]) {
50589 if (operation === INTERSECTION) {
50591 } else if (operation === DIFFERENCE) {
50593 } else if (operation === UNION || operation === XOR) {
50594 result = subject.concat(clipping);
50601 function _boolean(subject, clipping, operation) {
50602 if (typeof subject[0][0][0] === 'number') {
50603 subject = [subject];
50606 if (typeof clipping[0][0][0] === 'number') {
50607 clipping = [clipping];
50610 var trivial = trivialOperation(subject, clipping, operation);
50613 return trivial === EMPTY ? null : trivial;
50616 var sbbox = [Infinity, Infinity, -Infinity, -Infinity];
50617 var cbbox = [Infinity, Infinity, -Infinity, -Infinity]; // console.time('fill queue');
50619 var eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation); //console.timeEnd('fill queue');
50621 trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
50624 return trivial === EMPTY ? null : trivial;
50625 } // console.time('subdivide edges');
50628 var sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation); //console.timeEnd('subdivide edges');
50629 // console.time('connect vertices');
50631 var contours = connectEdges(sortedEvents); //console.timeEnd('connect vertices');
50632 // Convert contours to polygons
50636 for (var i = 0; i < contours.length; i++) {
50637 var contour = contours[i];
50639 if (contour.isExterior()) {
50640 // The exterior ring goes first
50641 var rings = [contour.points]; // Followed by holes if any
50643 for (var j = 0; j < contour.holeIds.length; j++) {
50644 var holeId = contour.holeIds[j];
50645 rings.push(contours[holeId].points);
50648 polygons.push(rings);
50655 function union(subject, clipping) {
50656 return _boolean(subject, clipping, UNION);
50659 var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
50661 var eLen = nBytes * 8 - mLen - 1;
50662 var eMax = (1 << eLen) - 1;
50663 var eBias = eMax >> 1;
50665 var i = isLE ? nBytes - 1 : 0;
50666 var d = isLE ? -1 : 1;
50667 var s = buffer[offset + i];
50669 e = s & (1 << -nBits) - 1;
50673 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
50675 m = e & (1 << -nBits) - 1;
50679 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
50683 } else if (e === eMax) {
50684 return m ? NaN : (s ? -1 : 1) * Infinity;
50686 m = m + Math.pow(2, mLen);
50690 return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
50693 var write$6 = function write(buffer, value, offset, isLE, mLen, nBytes) {
50695 var eLen = nBytes * 8 - mLen - 1;
50696 var eMax = (1 << eLen) - 1;
50697 var eBias = eMax >> 1;
50698 var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
50699 var i = isLE ? 0 : nBytes - 1;
50700 var d = isLE ? 1 : -1;
50701 var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
50702 value = Math.abs(value);
50704 if (isNaN(value) || value === Infinity) {
50705 m = isNaN(value) ? 1 : 0;
50708 e = Math.floor(Math.log(value) / Math.LN2);
50710 if (value * (c = Math.pow(2, -e)) < 1) {
50715 if (e + eBias >= 1) {
50718 value += rt * Math.pow(2, 1 - eBias);
50721 if (value * c >= 2) {
50726 if (e + eBias >= eMax) {
50729 } else if (e + eBias >= 1) {
50730 m = (value * c - 1) * Math.pow(2, mLen);
50733 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
50738 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
50743 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
50745 buffer[offset + i - d] |= s * 128;
50755 function Pbf(buf) {
50756 this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
50759 this.length = this.buf.length;
50762 Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
50764 Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
50766 Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
50768 Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
50770 var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
50771 SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string
50772 // data structures (which currently switch structure types at 12 bytes or more)
50774 var TEXT_DECODER_MIN_LENGTH = 12;
50775 var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
50777 destroy: function destroy() {
50780 // === READING =================================================================
50781 readFields: function readFields(readField, result, end) {
50782 end = end || this.length;
50784 while (this.pos < end) {
50785 var val = this.readVarint(),
50787 startPos = this.pos;
50788 this.type = val & 0x7;
50789 readField(tag, result, this);
50790 if (this.pos === startPos) this.skip(val);
50795 readMessage: function readMessage(readField, result) {
50796 return this.readFields(readField, result, this.readVarint() + this.pos);
50798 readFixed32: function readFixed32() {
50799 var val = readUInt32(this.buf, this.pos);
50803 readSFixed32: function readSFixed32() {
50804 var val = readInt32(this.buf, this.pos);
50808 // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
50809 readFixed64: function readFixed64() {
50810 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50814 readSFixed64: function readSFixed64() {
50815 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50819 readFloat: function readFloat() {
50820 var val = ieee754$1.read(this.buf, this.pos, true, 23, 4);
50824 readDouble: function readDouble() {
50825 var val = ieee754$1.read(this.buf, this.pos, true, 52, 8);
50829 readVarint: function readVarint(isSigned) {
50830 var buf = this.buf,
50833 b = buf[this.pos++];
50835 if (b < 0x80) return val;
50836 b = buf[this.pos++];
50837 val |= (b & 0x7f) << 7;
50838 if (b < 0x80) return val;
50839 b = buf[this.pos++];
50840 val |= (b & 0x7f) << 14;
50841 if (b < 0x80) return val;
50842 b = buf[this.pos++];
50843 val |= (b & 0x7f) << 21;
50844 if (b < 0x80) return val;
50846 val |= (b & 0x0f) << 28;
50847 return readVarintRemainder(val, isSigned, this);
50849 readVarint64: function readVarint64() {
50850 // for compatibility with v2.0.1
50851 return this.readVarint(true);
50853 readSVarint: function readSVarint() {
50854 var num = this.readVarint();
50855 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
50857 readBoolean: function readBoolean() {
50858 return Boolean(this.readVarint());
50860 readString: function readString() {
50861 var end = this.readVarint() + this.pos;
50862 var pos = this.pos;
50865 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
50866 // longer strings are fast with the built-in browser TextDecoder API
50867 return readUtf8TextDecoder(this.buf, pos, end);
50868 } // short strings are fast with our custom implementation
50871 return readUtf8(this.buf, pos, end);
50873 readBytes: function readBytes() {
50874 var end = this.readVarint() + this.pos,
50875 buffer = this.buf.subarray(this.pos, end);
50879 // verbose for performance reasons; doesn't affect gzipped size
50880 readPackedVarint: function readPackedVarint(arr, isSigned) {
50881 if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
50882 var end = readPackedEnd(this);
50885 while (this.pos < end) {
50886 arr.push(this.readVarint(isSigned));
50891 readPackedSVarint: function readPackedSVarint(arr) {
50892 if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
50893 var end = readPackedEnd(this);
50896 while (this.pos < end) {
50897 arr.push(this.readSVarint());
50902 readPackedBoolean: function readPackedBoolean(arr) {
50903 if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
50904 var end = readPackedEnd(this);
50907 while (this.pos < end) {
50908 arr.push(this.readBoolean());
50913 readPackedFloat: function readPackedFloat(arr) {
50914 if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
50915 var end = readPackedEnd(this);
50918 while (this.pos < end) {
50919 arr.push(this.readFloat());
50924 readPackedDouble: function readPackedDouble(arr) {
50925 if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
50926 var end = readPackedEnd(this);
50929 while (this.pos < end) {
50930 arr.push(this.readDouble());
50935 readPackedFixed32: function readPackedFixed32(arr) {
50936 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
50937 var end = readPackedEnd(this);
50940 while (this.pos < end) {
50941 arr.push(this.readFixed32());
50946 readPackedSFixed32: function readPackedSFixed32(arr) {
50947 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
50948 var end = readPackedEnd(this);
50951 while (this.pos < end) {
50952 arr.push(this.readSFixed32());
50957 readPackedFixed64: function readPackedFixed64(arr) {
50958 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
50959 var end = readPackedEnd(this);
50962 while (this.pos < end) {
50963 arr.push(this.readFixed64());
50968 readPackedSFixed64: function readPackedSFixed64(arr) {
50969 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
50970 var end = readPackedEnd(this);
50973 while (this.pos < end) {
50974 arr.push(this.readSFixed64());
50979 skip: function skip(val) {
50980 var type = val & 0x7;
50981 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);
50983 // === WRITING =================================================================
50984 writeTag: function writeTag(tag, type) {
50985 this.writeVarint(tag << 3 | type);
50987 realloc: function realloc(min) {
50988 var length = this.length || 16;
50990 while (length < this.pos + min) {
50994 if (length !== this.length) {
50995 var buf = new Uint8Array(length);
50998 this.length = length;
51001 finish: function finish() {
51002 this.length = this.pos;
51004 return this.buf.subarray(0, this.length);
51006 writeFixed32: function writeFixed32(val) {
51008 writeInt32(this.buf, val, this.pos);
51011 writeSFixed32: function writeSFixed32(val) {
51013 writeInt32(this.buf, val, this.pos);
51016 writeFixed64: function writeFixed64(val) {
51018 writeInt32(this.buf, val & -1, this.pos);
51019 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
51022 writeSFixed64: function writeSFixed64(val) {
51024 writeInt32(this.buf, val & -1, this.pos);
51025 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
51028 writeVarint: function writeVarint(val) {
51031 if (val > 0xfffffff || val < 0) {
51032 writeBigVarint(val, this);
51037 this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0);
51038 if (val <= 0x7f) return;
51039 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
51040 if (val <= 0x7f) return;
51041 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
51042 if (val <= 0x7f) return;
51043 this.buf[this.pos++] = val >>> 7 & 0x7f;
51045 writeSVarint: function writeSVarint(val) {
51046 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
51048 writeBoolean: function writeBoolean(val) {
51049 this.writeVarint(Boolean(val));
51051 writeString: function writeString(str) {
51053 this.realloc(str.length * 4);
51054 this.pos++; // reserve 1 byte for short string length
51056 var startPos = this.pos; // write the string directly to the buffer and see how much was written
51058 this.pos = writeUtf8(this.buf, str, this.pos);
51059 var len = this.pos - startPos;
51060 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
51062 this.pos = startPos - 1;
51063 this.writeVarint(len);
51066 writeFloat: function writeFloat(val) {
51068 ieee754$1.write(this.buf, val, this.pos, true, 23, 4);
51071 writeDouble: function writeDouble(val) {
51073 ieee754$1.write(this.buf, val, this.pos, true, 52, 8);
51076 writeBytes: function writeBytes(buffer) {
51077 var len = buffer.length;
51078 this.writeVarint(len);
51081 for (var i = 0; i < len; i++) {
51082 this.buf[this.pos++] = buffer[i];
51085 writeRawMessage: function writeRawMessage(fn, obj) {
51086 this.pos++; // reserve 1 byte for short message length
51087 // write the message directly to the buffer and see how much was written
51089 var startPos = this.pos;
51091 var len = this.pos - startPos;
51092 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
51094 this.pos = startPos - 1;
51095 this.writeVarint(len);
51098 writeMessage: function writeMessage(tag, fn, obj) {
51099 this.writeTag(tag, Pbf.Bytes);
51100 this.writeRawMessage(fn, obj);
51102 writePackedVarint: function writePackedVarint(tag, arr) {
51103 if (arr.length) this.writeMessage(tag, _writePackedVarint, arr);
51105 writePackedSVarint: function writePackedSVarint(tag, arr) {
51106 if (arr.length) this.writeMessage(tag, _writePackedSVarint, arr);
51108 writePackedBoolean: function writePackedBoolean(tag, arr) {
51109 if (arr.length) this.writeMessage(tag, _writePackedBoolean, arr);
51111 writePackedFloat: function writePackedFloat(tag, arr) {
51112 if (arr.length) this.writeMessage(tag, _writePackedFloat, arr);
51114 writePackedDouble: function writePackedDouble(tag, arr) {
51115 if (arr.length) this.writeMessage(tag, _writePackedDouble, arr);
51117 writePackedFixed32: function writePackedFixed32(tag, arr) {
51118 if (arr.length) this.writeMessage(tag, _writePackedFixed, arr);
51120 writePackedSFixed32: function writePackedSFixed32(tag, arr) {
51121 if (arr.length) this.writeMessage(tag, _writePackedSFixed, arr);
51123 writePackedFixed64: function writePackedFixed64(tag, arr) {
51124 if (arr.length) this.writeMessage(tag, _writePackedFixed2, arr);
51126 writePackedSFixed64: function writePackedSFixed64(tag, arr) {
51127 if (arr.length) this.writeMessage(tag, _writePackedSFixed2, arr);
51129 writeBytesField: function writeBytesField(tag, buffer) {
51130 this.writeTag(tag, Pbf.Bytes);
51131 this.writeBytes(buffer);
51133 writeFixed32Field: function writeFixed32Field(tag, val) {
51134 this.writeTag(tag, Pbf.Fixed32);
51135 this.writeFixed32(val);
51137 writeSFixed32Field: function writeSFixed32Field(tag, val) {
51138 this.writeTag(tag, Pbf.Fixed32);
51139 this.writeSFixed32(val);
51141 writeFixed64Field: function writeFixed64Field(tag, val) {
51142 this.writeTag(tag, Pbf.Fixed64);
51143 this.writeFixed64(val);
51145 writeSFixed64Field: function writeSFixed64Field(tag, val) {
51146 this.writeTag(tag, Pbf.Fixed64);
51147 this.writeSFixed64(val);
51149 writeVarintField: function writeVarintField(tag, val) {
51150 this.writeTag(tag, Pbf.Varint);
51151 this.writeVarint(val);
51153 writeSVarintField: function writeSVarintField(tag, val) {
51154 this.writeTag(tag, Pbf.Varint);
51155 this.writeSVarint(val);
51157 writeStringField: function writeStringField(tag, str) {
51158 this.writeTag(tag, Pbf.Bytes);
51159 this.writeString(str);
51161 writeFloatField: function writeFloatField(tag, val) {
51162 this.writeTag(tag, Pbf.Fixed32);
51163 this.writeFloat(val);
51165 writeDoubleField: function writeDoubleField(tag, val) {
51166 this.writeTag(tag, Pbf.Fixed64);
51167 this.writeDouble(val);
51169 writeBooleanField: function writeBooleanField(tag, val) {
51170 this.writeVarintField(tag, Boolean(val));
51174 function readVarintRemainder(l, s, p) {
51179 h = (b & 0x70) >> 4;
51180 if (b < 0x80) return toNum(l, h, s);
51182 h |= (b & 0x7f) << 3;
51183 if (b < 0x80) return toNum(l, h, s);
51185 h |= (b & 0x7f) << 10;
51186 if (b < 0x80) return toNum(l, h, s);
51188 h |= (b & 0x7f) << 17;
51189 if (b < 0x80) return toNum(l, h, s);
51191 h |= (b & 0x7f) << 24;
51192 if (b < 0x80) return toNum(l, h, s);
51194 h |= (b & 0x01) << 31;
51195 if (b < 0x80) return toNum(l, h, s);
51196 throw new Error('Expected varint not more than 10 bytes');
51199 function readPackedEnd(pbf) {
51200 return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
51203 function toNum(low, high, isSigned) {
51205 return high * 0x100000000 + (low >>> 0);
51208 return (high >>> 0) * 0x100000000 + (low >>> 0);
51211 function writeBigVarint(val, pbf) {
51215 low = val % 0x100000000 | 0;
51216 high = val / 0x100000000 | 0;
51218 low = ~(-val % 0x100000000);
51219 high = ~(-val / 0x100000000);
51221 if (low ^ 0xffffffff) {
51225 high = high + 1 | 0;
51229 if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
51230 throw new Error('Given varint doesn\'t fit into 10 bytes');
51234 writeBigVarintLow(low, high, pbf);
51235 writeBigVarintHigh(high, pbf);
51238 function writeBigVarintLow(low, high, pbf) {
51239 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51241 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51243 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51245 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51247 pbf.buf[pbf.pos] = low & 0x7f;
51250 function writeBigVarintHigh(high, pbf) {
51251 var lsb = (high & 0x07) << 4;
51252 pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0);
51254 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51256 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51258 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51260 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51262 pbf.buf[pbf.pos++] = high & 0x7f;
51265 function makeRoomForExtraLength(startPos, len, pbf) {
51266 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
51268 pbf.realloc(extraLen);
51270 for (var i = pbf.pos - 1; i >= startPos; i--) {
51271 pbf.buf[i + extraLen] = pbf.buf[i];
51275 function _writePackedVarint(arr, pbf) {
51276 for (var i = 0; i < arr.length; i++) {
51277 pbf.writeVarint(arr[i]);
51281 function _writePackedSVarint(arr, pbf) {
51282 for (var i = 0; i < arr.length; i++) {
51283 pbf.writeSVarint(arr[i]);
51287 function _writePackedFloat(arr, pbf) {
51288 for (var i = 0; i < arr.length; i++) {
51289 pbf.writeFloat(arr[i]);
51293 function _writePackedDouble(arr, pbf) {
51294 for (var i = 0; i < arr.length; i++) {
51295 pbf.writeDouble(arr[i]);
51299 function _writePackedBoolean(arr, pbf) {
51300 for (var i = 0; i < arr.length; i++) {
51301 pbf.writeBoolean(arr[i]);
51305 function _writePackedFixed(arr, pbf) {
51306 for (var i = 0; i < arr.length; i++) {
51307 pbf.writeFixed32(arr[i]);
51311 function _writePackedSFixed(arr, pbf) {
51312 for (var i = 0; i < arr.length; i++) {
51313 pbf.writeSFixed32(arr[i]);
51317 function _writePackedFixed2(arr, pbf) {
51318 for (var i = 0; i < arr.length; i++) {
51319 pbf.writeFixed64(arr[i]);
51323 function _writePackedSFixed2(arr, pbf) {
51324 for (var i = 0; i < arr.length; i++) {
51325 pbf.writeSFixed64(arr[i]);
51327 } // Buffer code below from https://github.com/feross/buffer, MIT-licensed
51330 function readUInt32(buf, pos) {
51331 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 0x1000000;
51334 function writeInt32(buf, val, pos) {
51336 buf[pos + 1] = val >>> 8;
51337 buf[pos + 2] = val >>> 16;
51338 buf[pos + 3] = val >>> 24;
51341 function readInt32(buf, pos) {
51342 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24);
51345 function readUtf8(buf, pos, end) {
51351 var c = null; // codepoint
51353 var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1;
51354 if (i + bytesPerSequence > end) break;
51357 if (bytesPerSequence === 1) {
51361 } else if (bytesPerSequence === 2) {
51364 if ((b1 & 0xC0) === 0x80) {
51365 c = (b0 & 0x1F) << 0x6 | b1 & 0x3F;
51371 } else if (bytesPerSequence === 3) {
51375 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
51376 c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | b2 & 0x3F;
51378 if (c <= 0x7FF || c >= 0xD800 && c <= 0xDFFF) {
51382 } else if (bytesPerSequence === 4) {
51387 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
51388 c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | b3 & 0x3F;
51390 if (c <= 0xFFFF || c >= 0x110000) {
51398 bytesPerSequence = 1;
51399 } else if (c > 0xFFFF) {
51401 str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
51402 c = 0xDC00 | c & 0x3FF;
51405 str += String.fromCharCode(c);
51406 i += bytesPerSequence;
51412 function readUtf8TextDecoder(buf, pos, end) {
51413 return utf8TextDecoder.decode(buf.subarray(pos, end));
51416 function writeUtf8(buf, str, pos) {
51417 for (var i = 0, c, lead; i < str.length; i++) {
51418 c = str.charCodeAt(i); // code point
51420 if (c > 0xD7FF && c < 0xE000) {
51429 c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
51433 if (c > 0xDBFF || i + 1 === str.length) {
51454 buf[pos++] = c >> 0x6 | 0xC0;
51457 buf[pos++] = c >> 0xC | 0xE0;
51459 buf[pos++] = c >> 0x12 | 0xF0;
51460 buf[pos++] = c >> 0xC & 0x3F | 0x80;
51463 buf[pos++] = c >> 0x6 & 0x3F | 0x80;
51466 buf[pos++] = c & 0x3F | 0x80;
51473 var pointGeometry = Point;
51475 * A standalone point geometry with useful accessor, comparison, and
51476 * modification methods.
51479 * @param {Number} x the x-coordinate. this could be longitude or screen
51480 * pixels, or any other sort of unit.
51481 * @param {Number} y the y-coordinate. this could be latitude or screen
51482 * pixels, or any other sort of unit.
51484 * var point = new Point(-77, 38);
51487 function Point(x, y) {
51492 Point.prototype = {
51494 * Clone this point, returning a new point that can be modified
51495 * without affecting the old one.
51496 * @return {Point} the clone
51498 clone: function clone() {
51499 return new Point(this.x, this.y);
51503 * Add this point's x & y coordinates to another point,
51504 * yielding a new point.
51505 * @param {Point} p the other point
51506 * @return {Point} output point
51508 add: function add(p) {
51509 return this.clone()._add(p);
51513 * Subtract this point's x & y coordinates to from point,
51514 * yielding a new point.
51515 * @param {Point} p the other point
51516 * @return {Point} output point
51518 sub: function sub(p) {
51519 return this.clone()._sub(p);
51523 * Multiply this point's x & y coordinates by point,
51524 * yielding a new point.
51525 * @param {Point} p the other point
51526 * @return {Point} output point
51528 multByPoint: function multByPoint(p) {
51529 return this.clone()._multByPoint(p);
51533 * Divide this point's x & y coordinates by point,
51534 * yielding a new point.
51535 * @param {Point} p the other point
51536 * @return {Point} output point
51538 divByPoint: function divByPoint(p) {
51539 return this.clone()._divByPoint(p);
51543 * Multiply this point's x & y coordinates by a factor,
51544 * yielding a new point.
51545 * @param {Point} k factor
51546 * @return {Point} output point
51548 mult: function mult(k) {
51549 return this.clone()._mult(k);
51553 * Divide this point's x & y coordinates by a factor,
51554 * yielding a new point.
51555 * @param {Point} k factor
51556 * @return {Point} output point
51558 div: function div(k) {
51559 return this.clone()._div(k);
51563 * Rotate this point around the 0, 0 origin by an angle a,
51565 * @param {Number} a angle to rotate around, in radians
51566 * @return {Point} output point
51568 rotate: function rotate(a) {
51569 return this.clone()._rotate(a);
51573 * Rotate this point around p point by an angle a,
51575 * @param {Number} a angle to rotate around, in radians
51576 * @param {Point} p Point to rotate around
51577 * @return {Point} output point
51579 rotateAround: function rotateAround(a, p) {
51580 return this.clone()._rotateAround(a, p);
51584 * Multiply this point by a 4x1 transformation matrix
51585 * @param {Array<Number>} m transformation matrix
51586 * @return {Point} output point
51588 matMult: function matMult(m) {
51589 return this.clone()._matMult(m);
51593 * Calculate this point but as a unit vector from 0, 0, meaning
51594 * that the distance from the resulting point to the 0, 0
51595 * coordinate will be equal to 1 and the angle from the resulting
51596 * point to the 0, 0 coordinate will be the same as before.
51597 * @return {Point} unit vector point
51599 unit: function unit() {
51600 return this.clone()._unit();
51604 * Compute a perpendicular point, where the new y coordinate
51605 * is the old x coordinate and the new x coordinate is the old y
51606 * coordinate multiplied by -1
51607 * @return {Point} perpendicular point
51609 perp: function perp() {
51610 return this.clone()._perp();
51614 * Return a version of this point with the x & y coordinates
51615 * rounded to integers.
51616 * @return {Point} rounded point
51618 round: function round() {
51619 return this.clone()._round();
51623 * Return the magitude of this point: this is the Euclidean
51624 * distance from the 0, 0 coordinate to this point's x and y
51626 * @return {Number} magnitude
51628 mag: function mag() {
51629 return Math.sqrt(this.x * this.x + this.y * this.y);
51633 * Judge whether this point is equal to another point, returning
51635 * @param {Point} other the other point
51636 * @return {boolean} whether the points are equal
51638 equals: function equals(other) {
51639 return this.x === other.x && this.y === other.y;
51643 * Calculate the distance from this point to another point
51644 * @param {Point} p the other point
51645 * @return {Number} distance
51647 dist: function dist(p) {
51648 return Math.sqrt(this.distSqr(p));
51652 * Calculate the distance from this point to another point,
51653 * without the square root step. Useful if you're comparing
51654 * relative distances.
51655 * @param {Point} p the other point
51656 * @return {Number} distance
51658 distSqr: function distSqr(p) {
51659 var dx = p.x - this.x,
51661 return dx * dx + dy * dy;
51665 * Get the angle from the 0, 0 coordinate to this point, in radians
51667 * @return {Number} angle
51669 angle: function angle() {
51670 return Math.atan2(this.y, this.x);
51674 * Get the angle from this point to another point, in radians
51675 * @param {Point} b the other point
51676 * @return {Number} angle
51678 angleTo: function angleTo(b) {
51679 return Math.atan2(this.y - b.y, this.x - b.x);
51683 * Get the angle between this point and another point, in radians
51684 * @param {Point} b the other point
51685 * @return {Number} angle
51687 angleWith: function angleWith(b) {
51688 return this.angleWithSep(b.x, b.y);
51692 * Find the angle of the two vectors, solving the formula for
51693 * the cross product a x b = |a||b|sin(θ) for θ.
51694 * @param {Number} x the x-coordinate
51695 * @param {Number} y the y-coordinate
51696 * @return {Number} the angle in radians
51698 angleWithSep: function angleWithSep(x, y) {
51699 return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
51701 _matMult: function _matMult(m) {
51702 var x = m[0] * this.x + m[1] * this.y,
51703 y = m[2] * this.x + m[3] * this.y;
51708 _add: function _add(p) {
51713 _sub: function _sub(p) {
51718 _mult: function _mult(k) {
51723 _div: function _div(k) {
51728 _multByPoint: function _multByPoint(p) {
51733 _divByPoint: function _divByPoint(p) {
51738 _unit: function _unit() {
51739 this._div(this.mag());
51743 _perp: function _perp() {
51749 _rotate: function _rotate(angle) {
51750 var cos = Math.cos(angle),
51751 sin = Math.sin(angle),
51752 x = cos * this.x - sin * this.y,
51753 y = sin * this.x + cos * this.y;
51758 _rotateAround: function _rotateAround(angle, p) {
51759 var cos = Math.cos(angle),
51760 sin = Math.sin(angle),
51761 x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
51762 y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
51767 _round: function _round() {
51768 this.x = Math.round(this.x);
51769 this.y = Math.round(this.y);
51774 * Construct a point from an array if necessary, otherwise if the input
51775 * is already a Point, or an unknown type, return it unchanged
51776 * @param {Array<Number>|Point|*} a any kind of input value
51777 * @return {Point} constructed point, or passed-through value.
51780 * var point = Point.convert([0, 1]);
51781 * // is equivalent to
51782 * var point = new Point(0, 1);
51785 Point.convert = function (a) {
51786 if (a instanceof Point) {
51790 if (Array.isArray(a)) {
51791 return new Point(a[0], a[1]);
51797 var vectortilefeature = VectorTileFeature;
51799 function VectorTileFeature(pbf, end, extent, keys, values) {
51801 this.properties = {};
51802 this.extent = extent;
51803 this.type = 0; // Private
51806 this._geometry = -1;
51808 this._values = values;
51809 pbf.readFields(readFeature, this, end);
51812 function readFeature(tag, feature, pbf) {
51813 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;
51816 function readTag(pbf, feature) {
51817 var end = pbf.readVarint() + pbf.pos;
51819 while (pbf.pos < end) {
51820 var key = feature._keys[pbf.readVarint()],
51821 value = feature._values[pbf.readVarint()];
51823 feature.properties[key] = value;
51827 VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
51829 VectorTileFeature.prototype.loadGeometry = function () {
51830 var pbf = this._pbf;
51831 pbf.pos = this._geometry;
51832 var end = pbf.readVarint() + pbf.pos,
51840 while (pbf.pos < end) {
51842 var cmdLen = pbf.readVarint();
51843 cmd = cmdLen & 0x7;
51844 length = cmdLen >> 3;
51849 if (cmd === 1 || cmd === 2) {
51850 x += pbf.readSVarint();
51851 y += pbf.readSVarint();
51855 if (line) lines.push(line);
51859 line.push(new pointGeometry(x, y));
51860 } else if (cmd === 7) {
51861 // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
51863 line.push(line[0].clone()); // closePolygon
51866 throw new Error('unknown command ' + cmd);
51870 if (line) lines.push(line);
51874 VectorTileFeature.prototype.bbox = function () {
51875 var pbf = this._pbf;
51876 pbf.pos = this._geometry;
51877 var end = pbf.readVarint() + pbf.pos,
51887 while (pbf.pos < end) {
51889 var cmdLen = pbf.readVarint();
51890 cmd = cmdLen & 0x7;
51891 length = cmdLen >> 3;
51896 if (cmd === 1 || cmd === 2) {
51897 x += pbf.readSVarint();
51898 y += pbf.readSVarint();
51899 if (x < x1) x1 = x;
51900 if (x > x2) x2 = x;
51901 if (y < y1) y1 = y;
51902 if (y > y2) y2 = y;
51903 } else if (cmd !== 7) {
51904 throw new Error('unknown command ' + cmd);
51908 return [x1, y1, x2, y2];
51911 VectorTileFeature.prototype.toGeoJSON = function (x, y, z) {
51912 var size = this.extent * Math.pow(2, z),
51913 x0 = this.extent * x,
51914 y0 = this.extent * y,
51915 coords = this.loadGeometry(),
51916 type = VectorTileFeature.types[this.type],
51920 function project(line) {
51921 for (var j = 0; j < line.length; j++) {
51923 y2 = 180 - (p.y + y0) * 360 / size;
51924 line[j] = [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90];
51928 switch (this.type) {
51932 for (i = 0; i < coords.length; i++) {
51933 points[i] = coords[i][0];
51941 for (i = 0; i < coords.length; i++) {
51942 project(coords[i]);
51948 coords = classifyRings(coords);
51950 for (i = 0; i < coords.length; i++) {
51951 for (j = 0; j < coords[i].length; j++) {
51952 project(coords[i][j]);
51959 if (coords.length === 1) {
51960 coords = coords[0];
51962 type = 'Multi' + type;
51969 coordinates: coords
51971 properties: this.properties
51974 if ('id' in this) {
51975 result.id = this.id;
51979 }; // classifies an array of rings into polygons with outer rings and holes
51982 function classifyRings(rings) {
51983 var len = rings.length;
51984 if (len <= 1) return [rings];
51989 for (var i = 0; i < len; i++) {
51990 var area = signedArea$1(rings[i]);
51991 if (area === 0) continue;
51992 if (ccw === undefined) ccw = area < 0;
51994 if (ccw === area < 0) {
51995 if (polygon) polygons.push(polygon);
51996 polygon = [rings[i]];
51998 polygon.push(rings[i]);
52002 if (polygon) polygons.push(polygon);
52006 function signedArea$1(ring) {
52009 for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
52012 sum += (p2.x - p1.x) * (p1.y + p2.y);
52018 var vectortilelayer = VectorTileLayer;
52020 function VectorTileLayer(pbf, end) {
52024 this.extent = 4096;
52025 this.length = 0; // Private
52030 this._features = [];
52031 pbf.readFields(readLayer, this, end);
52032 this.length = this._features.length;
52035 function readLayer(tag, layer, pbf) {
52036 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));
52039 function readValueMessage(pbf) {
52041 end = pbf.readVarint() + pbf.pos;
52043 while (pbf.pos < end) {
52044 var tag = pbf.readVarint() >> 3;
52045 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;
52049 } // return feature `i` from this layer as a `VectorTileFeature`
52052 VectorTileLayer.prototype.feature = function (i) {
52053 if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
52054 this._pbf.pos = this._features[i];
52056 var end = this._pbf.readVarint() + this._pbf.pos;
52058 return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
52061 var vectortile = VectorTile;
52063 function VectorTile(pbf, end) {
52064 this.layers = pbf.readFields(readTile, {}, end);
52067 function readTile(tag, layers, pbf) {
52069 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
52070 if (layer.length) layers[layer.name] = layer;
52074 var VectorTile$1 = vectortile;
52075 var VectorTileFeature$1 = vectortilefeature;
52076 var VectorTileLayer$1 = vectortilelayer;
52078 VectorTile: VectorTile$1,
52079 VectorTileFeature: VectorTileFeature$1,
52080 VectorTileLayer: VectorTileLayer$1
52083 var tiler$7 = utilTiler().tileSize(512).margin(1);
52084 var dispatch$8 = dispatch('loadedData');
52088 function abortRequest$7(controller) {
52089 controller.abort();
52092 function vtToGeoJSON(data, tile, mergeCache) {
52093 var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
52094 var layers = Object.keys(vectorTile$1.layers);
52096 if (!Array.isArray(layers)) {
52101 layers.forEach(function (layerID) {
52102 var layer = vectorTile$1.layers[layerID];
52105 for (var i = 0; i < layer.length; i++) {
52106 var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
52107 var geometry = feature.geometry; // Treat all Polygons as MultiPolygons
52109 if (geometry.type === 'Polygon') {
52110 geometry.type = 'MultiPolygon';
52111 geometry.coordinates = [geometry.coordinates];
52114 var isClipped = false; // Clip to tile bounds
52116 if (geometry.type === 'MultiPolygon') {
52117 var featureClip = turf_bboxClip(feature, tile.extent.rectangle());
52119 if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
52120 // feature = featureClip;
52124 if (!feature.geometry.coordinates.length) continue; // not actually on this tile
52126 if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile
52127 } // Generate some unique IDs and add some metadata
52130 var featurehash = utilHashcode(fastJsonStableStringify(feature));
52131 var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
52132 feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
52133 feature.__featurehash__ = featurehash;
52134 feature.__propertyhash__ = propertyhash;
52135 features.push(feature); // Clipped Polygons at same zoom with identical properties can get merged
52137 if (isClipped && geometry.type === 'MultiPolygon') {
52138 var merged = mergeCache[propertyhash];
52140 if (merged && merged.length) {
52141 var other = merged[0];
52142 var coords = union(feature.geometry.coordinates, other.geometry.coordinates);
52144 if (!coords || !coords.length) {
52145 continue; // something failed in martinez union
52148 merged.push(feature);
52150 for (var j = 0; j < merged.length; j++) {
52151 // all these features get...
52152 merged[j].geometry.coordinates = coords; // same coords
52154 merged[j].__featurehash__ = featurehash; // same hash, so deduplication works
52157 mergeCache[propertyhash] = [feature];
52166 function loadTile(source, tile) {
52167 if (source.loaded[tile.id] || source.inflight[tile.id]) return;
52168 var url = source.template.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]) // TMS-flipped y coordinate
52169 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1).replace(/\{z(oom)?\}/, tile.xyz[2]).replace(/\{switch:([^}]+)\}/, function (s, r) {
52170 var subdomains = r.split(',');
52171 return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
52173 var controller = new AbortController();
52174 source.inflight[tile.id] = controller;
52176 signal: controller.signal
52177 }).then(function (response) {
52178 if (!response.ok) {
52179 throw new Error(response.status + ' ' + response.statusText);
52182 source.loaded[tile.id] = [];
52183 delete source.inflight[tile.id];
52184 return response.arrayBuffer();
52185 }).then(function (data) {
52187 throw new Error('No Data');
52190 var z = tile.xyz[2];
52192 if (!source.canMerge[z]) {
52193 source.canMerge[z] = {}; // initialize mergeCache
52196 source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
52197 dispatch$8.call('loadedData');
52198 })["catch"](function () {
52199 source.loaded[tile.id] = [];
52200 delete source.inflight[tile.id];
52204 var serviceVectorTile = {
52205 init: function init() {
52210 this.event = utilRebind(this, dispatch$8, 'on');
52212 reset: function reset() {
52213 for (var sourceID in _vtCache) {
52214 var source = _vtCache[sourceID];
52216 if (source && source.inflight) {
52217 Object.values(source.inflight).forEach(abortRequest$7);
52223 addSource: function addSource(sourceID, template) {
52224 _vtCache[sourceID] = {
52225 template: template,
52230 return _vtCache[sourceID];
52232 data: function data(sourceID, projection) {
52233 var source = _vtCache[sourceID];
52234 if (!source) return [];
52235 var tiles = tiler$7.getTiles(projection);
52239 for (var i = 0; i < tiles.length; i++) {
52240 var features = source.loaded[tiles[i].id];
52241 if (!features || !features.length) continue;
52243 for (var j = 0; j < features.length; j++) {
52244 var feature = features[j];
52245 var hash = feature.__featurehash__;
52246 if (seen[hash]) continue;
52247 seen[hash] = true; // return a shallow copy, because the hash may change
52248 // later if this feature gets merged with another
52250 results.push(Object.assign({}, feature)); // shallow copy
52256 loadTiles: function loadTiles(sourceID, template, projection) {
52257 var source = _vtCache[sourceID];
52260 source = this.addSource(sourceID, template);
52263 var tiles = tiler$7.getTiles(projection); // abort inflight requests that are no longer needed
52265 Object.keys(source.inflight).forEach(function (k) {
52266 var wanted = tiles.find(function (tile) {
52267 return k === tile.id;
52271 abortRequest$7(source.inflight[k]);
52272 delete source.inflight[k];
52275 tiles.forEach(function (tile) {
52276 loadTile(source, tile);
52279 cache: function cache() {
52284 var apibase$3 = 'https://www.wikidata.org/w/api.php?';
52285 var _wikidataCache = {};
52286 var serviceWikidata = {
52287 init: function init() {},
52288 reset: function reset() {
52289 _wikidataCache = {};
52291 // Search for Wikidata items matching the query
52292 itemsForSearchQuery: function itemsForSearchQuery(query, callback) {
52294 if (callback) callback('No query', {});
52298 var lang = this.languagesToQuery()[0];
52299 var url = apibase$3 + utilQsString({
52300 action: 'wbsearchentities',
52305 // the language to search
52307 // the language for the label and description in the result
52312 d3_json(url).then(function (result) {
52313 if (result && result.error) {
52314 throw new Error(result.error);
52317 if (callback) callback(null, result.search || {});
52318 })["catch"](function (err) {
52319 if (callback) callback(err.message, {});
52322 // Given a Wikipedia language and article title,
52323 // return an array of corresponding Wikidata entities.
52324 itemsByTitle: function itemsByTitle(lang, title, callback) {
52326 if (callback) callback('No title', {});
52330 lang = lang || 'en';
52331 var url = apibase$3 + utilQsString({
52332 action: 'wbgetentities',
52335 sites: lang.replace(/-/g, '_') + 'wiki',
52338 // shrink response by filtering to one language
52341 d3_json(url).then(function (result) {
52342 if (result && result.error) {
52343 throw new Error(result.error);
52346 if (callback) callback(null, result.entities || {});
52347 })["catch"](function (err) {
52348 if (callback) callback(err.message, {});
52351 languagesToQuery: function languagesToQuery() {
52352 return _mainLocalizer.localeCodes().map(function (code) {
52353 return code.toLowerCase();
52354 }).filter(function (code) {
52355 // HACK: en-us isn't a wikidata language. We should really be filtering by
52356 // the languages known to be supported by wikidata.
52357 return code !== 'en-us';
52360 entityByQID: function entityByQID(qid, callback) {
52362 callback('No qid', {});
52366 if (_wikidataCache[qid]) {
52367 if (callback) callback(null, _wikidataCache[qid]);
52371 var langs = this.languagesToQuery();
52372 var url = apibase$3 + utilQsString({
52373 action: 'wbgetentities',
52377 props: 'labels|descriptions|claims|sitelinks',
52378 sitefilter: langs.map(function (d) {
52381 languages: langs.join('|'),
52382 languagefallback: 1,
52385 d3_json(url).then(function (result) {
52386 if (result && result.error) {
52387 throw new Error(result.error);
52390 if (callback) callback(null, result.entities[qid] || {});
52391 })["catch"](function (err) {
52392 if (callback) callback(err.message, {});
52395 // Pass `params` object of the form:
52397 // qid: 'string' // brand wikidata (e.g. 'Q37158')
52400 // Get an result object used to display tag documentation
52402 // title: 'string',
52403 // description: 'string',
52404 // editURL: 'string',
52405 // imageURL: 'string',
52406 // wiki: { title: 'string', text: 'string', url: 'string' }
52409 getDocs: function getDocs(params, callback) {
52410 var langs = this.languagesToQuery();
52411 this.entityByQID(params.qid, function (err, entity) {
52412 if (err || !entity) {
52413 callback(err || 'No entity');
52421 var code = langs[i];
52423 if (entity.descriptions[code] && entity.descriptions[code].language === code) {
52424 description = entity.descriptions[code];
52429 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
52433 description: description ? description.value : '',
52434 descriptionLocaleCode: description ? description.language : '',
52435 editURL: 'https://www.wikidata.org/wiki/' + entity.id
52438 if (entity.claims) {
52439 var imageroot = 'https://commons.wikimedia.org/w/index.php';
52440 var props = ['P154', 'P18']; // logo image, image
52444 for (i = 0; i < props.length; i++) {
52445 prop = entity.claims[props[i]];
52447 if (prop && Object.keys(prop).length > 0) {
52448 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
52451 result.imageURL = imageroot + '?' + utilQsString({
52452 title: 'Special:Redirect/file/' + image,
52461 if (entity.sitelinks) {
52462 var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en'; // must be one of these that we requested..
52464 for (i = 0; i < langs.length; i++) {
52465 // check each, in order of preference
52466 var w = langs[i] + 'wiki';
52468 if (entity.sitelinks[w]) {
52469 var title = entity.sitelinks[w].title;
52470 var tKey = 'inspector.wiki_reference';
52472 if (!englishLocale && langs[i] === 'en') {
52473 // user's locale isn't English but
52474 tKey = 'inspector.wiki_en_reference'; // we are sending them to enwiki anyway..
52480 url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
52487 callback(null, result);
52492 var endpoint = 'https://en.wikipedia.org/w/api.php?';
52493 var serviceWikipedia = {
52494 init: function init() {},
52495 reset: function reset() {},
52496 search: function search(lang, query, callback) {
52498 if (callback) callback('No Query', []);
52502 lang = lang || 'en';
52503 var url = endpoint.replace('en', lang) + utilQsString({
52507 srinfo: 'suggestion',
52512 d3_json(url).then(function (result) {
52513 if (result && result.error) {
52514 throw new Error(result.error);
52515 } else if (!result || !result.query || !result.query.search) {
52516 throw new Error('No Results');
52520 var titles = result.query.search.map(function (d) {
52523 callback(null, titles);
52525 })["catch"](function (err) {
52526 if (callback) callback(err, []);
52529 suggestions: function suggestions(lang, query, callback) {
52531 if (callback) callback('', []);
52535 lang = lang || 'en';
52536 var url = endpoint.replace('en', lang) + utilQsString({
52537 action: 'opensearch',
52544 d3_json(url).then(function (result) {
52545 if (result && result.error) {
52546 throw new Error(result.error);
52547 } else if (!result || result.length < 2) {
52548 throw new Error('No Results');
52551 if (callback) callback(null, result[1] || []);
52552 })["catch"](function (err) {
52553 if (callback) callback(err.message, []);
52556 translations: function translations(lang, title, callback) {
52558 if (callback) callback('No Title');
52562 var url = endpoint.replace('en', lang) + utilQsString({
52570 d3_json(url).then(function (result) {
52571 if (result && result.error) {
52572 throw new Error(result.error);
52573 } else if (!result || !result.query || !result.query.pages) {
52574 throw new Error('No Results');
52578 var list = result.query.pages[Object.keys(result.query.pages)[0]];
52579 var translations = {};
52581 if (list && list.langlinks) {
52582 list.langlinks.forEach(function (d) {
52583 translations[d.lang] = d['*'];
52587 callback(null, translations);
52589 })["catch"](function (err) {
52590 if (callback) callback(err.message);
52596 geocoder: serviceNominatim,
52597 keepRight: serviceKeepRight,
52598 improveOSM: serviceImproveOSM,
52599 osmose: serviceOsmose,
52600 mapillary: serviceMapillary,
52601 openstreetcam: serviceOpenstreetcam,
52603 osmWikibase: serviceOsmWikibase,
52604 maprules: serviceMapRules,
52605 streetside: serviceStreetside,
52606 taginfo: serviceTaginfo,
52607 vectorTile: serviceVectorTile,
52608 wikidata: serviceWikidata,
52609 wikipedia: serviceWikipedia
52612 function svgIcon(name, svgklass, useklass) {
52613 return function drawIcon(selection) {
52614 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);
52618 function uiNoteComments() {
52621 function noteComments(selection) {
52622 if (_note.isNew()) return; // don't draw .comments-container
52624 var comments = selection.selectAll('.comments-container').data([0]);
52625 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments);
52626 var commentEnter = comments.selectAll('.comment').data(_note.comments).enter().append('div').attr('class', 'comment');
52627 commentEnter.append('div').attr('class', function (d) {
52628 return 'comment-avatar user-' + d.uid;
52629 }).call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
52630 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
52631 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
52632 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
52633 var selection = select(this);
52634 var osm = services.osm;
52636 if (osm && d.user) {
52637 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.user)).attr('target', '_blank');
52640 selection.html(function (d) {
52641 return d.user || _t.html('note.anonymous');
52644 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
52645 return _t('note.status.' + d.action, {
52646 when: localeDateString(d.date)
52649 mainEnter.append('div').attr('class', 'comment-text').html(function (d) {
52651 }).selectAll('a').attr('rel', 'noopener nofollow').attr('target', '_blank');
52652 comments.call(replaceAvatars);
52655 function replaceAvatars(selection) {
52656 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
52657 var osm = services.osm;
52658 if (showThirdPartyIcons !== 'true' || !osm) return;
52659 var uids = {}; // gather uids in the comment thread
52661 _note.comments.forEach(function (d) {
52662 if (d.uid) uids[d.uid] = true;
52665 Object.keys(uids).forEach(function (uid) {
52666 osm.loadUser(uid, function (err, user) {
52667 if (!user || !user.image_url) return;
52668 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);
52673 function localeDateString(s) {
52674 if (!s) return null;
52680 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
52682 var d = new Date(s);
52683 if (isNaN(d.getTime())) return null;
52684 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
52687 noteComments.note = function (val) {
52688 if (!arguments.length) return _note;
52690 return noteComments;
52693 return noteComments;
52696 function uiNoteHeader() {
52699 function noteHeader(selection) {
52700 var header = selection.selectAll('.note-header').data(_note ? [_note] : [], function (d) {
52701 return d.status + d.id;
52703 header.exit().remove();
52704 var headerEnter = header.enter().append('div').attr('class', 'note-header');
52705 var iconEnter = headerEnter.append('div').attr('class', function (d) {
52706 return 'note-header-icon ' + d.status;
52707 }).classed('new', function (d) {
52710 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-note', 'note-fill'));
52711 iconEnter.each(function (d) {
52712 var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : d.status === 'open' ? 'close' : 'apply');
52713 iconEnter.append('div').attr('class', 'note-icon-annotation').call(svgIcon(statusIcon, 'icon-annotation'));
52715 headerEnter.append('div').attr('class', 'note-header-label').html(function (d) {
52716 if (_note.isNew()) {
52717 return _t('note.new');
52720 return _t('note.note') + ' ' + d.id + ' ' + (d.status === 'closed' ? _t('note.closed') : '');
52724 noteHeader.note = function (val) {
52725 if (!arguments.length) return _note;
52733 function uiNoteReport() {
52736 function noteReport(selection) {
52739 if (services.osm && _note instanceof osmNote && !_note.isNew()) {
52740 url = services.osm.noteReportURL(_note);
52743 var link = selection.selectAll('.note-report').data(url ? [url] : []); // exit
52745 link.exit().remove(); // enter
52747 var linkEnter = link.enter().append('a').attr('class', 'note-report').attr('target', '_blank').attr('href', function (d) {
52749 }).call(svgIcon('#iD-icon-out-link', 'inline'));
52750 linkEnter.append('span').html(_t.html('note.report'));
52753 noteReport.note = function (val) {
52754 if (!arguments.length) return _note;
52762 function uiViewOnOSM(context) {
52763 var _what; // an osmEntity or osmNote
52766 function viewOnOSM(selection) {
52769 if (_what instanceof osmEntity) {
52770 url = context.connection().entityURL(_what);
52771 } else if (_what instanceof osmNote) {
52772 url = context.connection().noteURL(_what);
52775 var data = !_what || _what.isNew() ? [] : [_what];
52776 var link = selection.selectAll('.view-on-osm').data(data, function (d) {
52780 link.exit().remove(); // enter
52782 var linkEnter = link.enter().append('a').attr('class', 'view-on-osm').attr('target', '_blank').attr('href', url).call(svgIcon('#iD-icon-out-link', 'inline'));
52783 linkEnter.append('span').html(_t.html('inspector.view_on_osm'));
52786 viewOnOSM.what = function (_) {
52787 if (!arguments.length) return _what;
52795 function uiNoteEditor(context) {
52796 var dispatch$1 = dispatch('change');
52797 var noteComments = uiNoteComments();
52798 var noteHeader = uiNoteHeader(); // var formFields = uiFormFields(context);
52802 var _newNote; // var _fieldsArr;
52805 function noteEditor(selection) {
52806 var header = selection.selectAll('.header').data([0]);
52807 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
52808 headerEnter.append('button').attr('class', 'close').on('click', function () {
52809 context.enter(modeBrowse(context));
52810 }).call(svgIcon('#iD-icon-close'));
52811 headerEnter.append('h3').html(_t.html('note.title'));
52812 var body = selection.selectAll('.body').data([0]);
52813 body = body.enter().append('div').attr('class', 'body').merge(body);
52814 var editor = body.selectAll('.note-editor').data([0]);
52815 editor.enter().append('div').attr('class', 'modal-section note-editor').merge(editor).call(noteHeader.note(_note)).call(noteComments.note(_note)).call(noteSaveSection);
52816 var footer = selection.selectAll('.footer').data([0]);
52817 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
52819 var osm = services.osm;
52822 osm.on('change.note-save', function () {
52823 selection.call(noteEditor);
52828 function noteSaveSection(selection) {
52829 var isSelected = _note && _note.id === context.selectedNoteID();
52831 var noteSave = selection.selectAll('.note-save').data(isSelected ? [_note] : [], function (d) {
52832 return d.status + d.id;
52835 noteSave.exit().remove(); // enter
52837 var noteSaveEnter = noteSave.enter().append('div').attr('class', 'note-save save-section cf'); // // if new note, show categories to pick from
52838 // if (_note.isNew()) {
52839 // var presets = presetManager;
52840 // // NOTE: this key isn't a age and therefore there is no documentation (yet)
52842 // uiField(context, presets.field('category'), null, { show: true, revert: false }),
52844 // _fieldsArr.forEach(function(field) {
52846 // .on('change', changeCategory);
52850 // .attr('class', 'note-category')
52851 // .call(formFields.fieldsArr(_fieldsArr));
52853 // function changeCategory() {
52854 // // NOTE: perhaps there is a better way to get value
52855 // var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
52856 // // store the unsaved category with the note itself
52857 // _note = _note.update({ newCategory: val });
52858 // var osm = services.osm;
52860 // osm.replaceNote(_note); // update note cache
52863 // .call(noteSaveButtons);
52866 noteSaveEnter.append('h4').attr('class', '.note-save-header').html(function () {
52867 return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
52869 var commentTextarea = noteSaveEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('note.inputPlaceholder')).attr('maxlength', 1000).property('value', function (d) {
52870 return d.newComment;
52871 }).call(utilNoAuto).on('keydown.note-input', keydown).on('input.note-input', changeInput).on('blur.note-input', changeInput);
52873 if (!commentTextarea.empty() && _newNote) {
52874 // autofocus the comment field for new notes
52875 commentTextarea.node().focus();
52879 noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons); // fast submit if user presses cmd+enter
52881 function keydown(d3_event) {
52882 if (!(d3_event.keyCode === 13 && // ↩ Return
52883 d3_event.metaKey)) return;
52884 var osm = services.osm;
52886 var hasAuth = osm.authenticated();
52887 if (!hasAuth) return;
52888 if (!_note.newComment) return;
52889 d3_event.preventDefault();
52890 select(this).on('keydown.note-input', null); // focus on button and submit
52892 window.setTimeout(function () {
52893 if (_note.isNew()) {
52894 noteSave.selectAll('.save-button').node().focus();
52897 noteSave.selectAll('.comment-button').node().focus();
52903 function changeInput() {
52904 var input = select(this);
52905 var val = input.property('value').trim() || undefined; // store the unsaved comment with the note itself
52907 _note = _note.update({
52910 var osm = services.osm;
52913 osm.replaceNote(_note); // update note cache
52916 noteSave.call(noteSaveButtons);
52920 function userDetails(selection) {
52921 var detailSection = selection.selectAll('.detail-section').data([0]);
52922 detailSection = detailSection.enter().append('div').attr('class', 'detail-section').merge(detailSection);
52923 var osm = services.osm;
52924 if (!osm) return; // Add warning if user is not logged in
52926 var hasAuth = osm.authenticated();
52927 var authWarning = detailSection.selectAll('.auth-warning').data(hasAuth ? [] : [0]);
52928 authWarning.exit().transition().duration(200).style('opacity', 0).remove();
52929 var authEnter = authWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning auth-warning').style('opacity', 0);
52930 authEnter.call(svgIcon('#iD-icon-alert', 'inline'));
52931 authEnter.append('span').html(_t.html('note.login'));
52932 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) {
52933 d3_event.preventDefault();
52934 osm.authenticate();
52936 authEnter.transition().duration(200).style('opacity', 1);
52937 var prose = detailSection.selectAll('.note-save-prose').data(hasAuth ? [0] : []);
52938 prose.exit().remove();
52939 prose = prose.enter().append('p').attr('class', 'note-save-prose').html(_t.html('note.upload_explanation')).merge(prose);
52940 osm.userDetails(function (err, user) {
52942 var userLink = select(document.createElement('div'));
52944 if (user.image_url) {
52945 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
52948 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
52949 prose.html(_t.html('note.upload_explanation_with_user', {
52950 user: userLink.html()
52955 function noteSaveButtons(selection) {
52956 var osm = services.osm;
52957 var hasAuth = osm && osm.authenticated();
52959 var isSelected = _note && _note.id === context.selectedNoteID();
52961 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_note] : [], function (d) {
52962 return d.status + d.id;
52965 buttonSection.exit().remove(); // enter
52967 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
52969 if (_note.isNew()) {
52970 buttonEnter.append('button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
52971 buttonEnter.append('button').attr('class', 'button save-button action').html(_t.html('note.save'));
52973 buttonEnter.append('button').attr('class', 'button status-button action');
52974 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('note.comment'));
52978 buttonSection = buttonSection.merge(buttonEnter);
52979 buttonSection.select('.cancel-button') // select and propagate data
52980 .on('click.cancel', clickCancel);
52981 buttonSection.select('.save-button') // select and propagate data
52982 .attr('disabled', isSaveDisabled).on('click.save', clickSave);
52983 buttonSection.select('.status-button') // select and propagate data
52984 .attr('disabled', hasAuth ? null : true).html(function (d) {
52985 var action = d.status === 'open' ? 'close' : 'open';
52986 var andComment = d.newComment ? '_comment' : '';
52987 return _t('note.' + action + andComment);
52988 }).on('click.status', clickStatus);
52989 buttonSection.select('.comment-button') // select and propagate data
52990 .attr('disabled', isSaveDisabled).on('click.comment', clickComment);
52992 function isSaveDisabled(d) {
52993 return hasAuth && d.status === 'open' && d.newComment ? null : true;
52997 function clickCancel(d3_event, d) {
52998 this.blur(); // avoid keeping focus on the button - #4641
53000 var osm = services.osm;
53006 context.enter(modeBrowse(context));
53007 dispatch$1.call('change');
53010 function clickSave(d3_event, d) {
53011 this.blur(); // avoid keeping focus on the button - #4641
53013 var osm = services.osm;
53016 osm.postNoteCreate(d, function (err, note) {
53017 dispatch$1.call('change', note);
53022 function clickStatus(d3_event, d) {
53023 this.blur(); // avoid keeping focus on the button - #4641
53025 var osm = services.osm;
53028 var setStatus = d.status === 'open' ? 'closed' : 'open';
53029 osm.postNoteUpdate(d, setStatus, function (err, note) {
53030 dispatch$1.call('change', note);
53035 function clickComment(d3_event, d) {
53036 this.blur(); // avoid keeping focus on the button - #4641
53038 var osm = services.osm;
53041 osm.postNoteUpdate(d, d.status, function (err, note) {
53042 dispatch$1.call('change', note);
53047 noteEditor.note = function (val) {
53048 if (!arguments.length) return _note;
53053 noteEditor.newNote = function (val) {
53054 if (!arguments.length) return _newNote;
53059 return utilRebind(noteEditor, dispatch$1, 'on');
53062 function modeSelectNote(context, selectedNoteID) {
53068 var _keybinding = utilKeybinding('select-note');
53070 var _noteEditor = uiNoteEditor(context).on('change', function () {
53071 context.map().pan([0, 0]); // trigger a redraw
53073 var note = checkSelectedID();
53075 context.ui().sidebar.show(_noteEditor.note(note));
53078 var _behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
53079 var _newFeature = false;
53081 function checkSelectedID() {
53082 if (!services.osm) return;
53083 var note = services.osm.getNote(selectedNoteID);
53086 context.enter(modeBrowse(context));
53090 } // class the note as selected, or return to browse mode if the note is gone
53093 function selectNote(d3_event, drawn) {
53094 if (!checkSelectedID()) return;
53095 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
53097 if (selection.empty()) {
53098 // Return to browse mode if selected DOM elements have
53099 // disappeared because the user moved them out of view..
53100 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
53102 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
53103 context.enter(modeBrowse(context));
53106 selection.classed('selected', true);
53107 context.selectedNoteID(selectedNoteID);
53112 if (context.container().select('.combobox').size()) return;
53113 context.enter(modeBrowse(context));
53116 mode.zoomToSelected = function () {
53117 if (!services.osm) return;
53118 var note = services.osm.getNote(selectedNoteID);
53121 context.map().centerZoomEase(note.loc, 20);
53125 mode.newFeature = function (val) {
53126 if (!arguments.length) return _newFeature;
53131 mode.enter = function () {
53132 var note = checkSelectedID();
53135 _behaviors.forEach(context.install);
53137 _keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
53139 select(document).call(_keybinding);
53141 var sidebar = context.ui().sidebar;
53142 sidebar.show(_noteEditor.note(note).newNote(_newFeature)); // expand the sidebar, avoid obscuring the note if needed
53144 sidebar.expand(sidebar.intersects(note.extent()));
53145 context.map().on('drawn.select', selectNote);
53148 mode.exit = function () {
53149 _behaviors.forEach(context.uninstall);
53151 select(document).call(_keybinding.unbind);
53152 context.surface().selectAll('.layer-notes .selected').classed('selected hover', false);
53153 context.map().on('drawn.select', null);
53154 context.ui().sidebar.hide();
53155 context.selectedNoteID(null);
53161 function modeDragNote(context) {
53166 var edit = behaviorEdit(context);
53168 var _nudgeInterval;
53172 var _note; // most current note.. dragged note may have stale datum.
53175 function startNudge(d3_event, nudge) {
53176 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
53177 _nudgeInterval = window.setInterval(function () {
53178 context.map().pan(nudge);
53179 doMove(d3_event, nudge);
53183 function stopNudge() {
53184 if (_nudgeInterval) {
53185 window.clearInterval(_nudgeInterval);
53186 _nudgeInterval = null;
53190 function origin(note) {
53191 return context.projection(note.loc);
53194 function start(d3_event, note) {
53196 var osm = services.osm;
53199 // Get latest note from cache.. The marker may have a stale datum bound to it
53200 // and dragging it around can sometimes delete the users note comment.
53201 _note = osm.getNote(_note.id);
53204 context.surface().selectAll('.note-' + _note.id).classed('active', true);
53205 context.perform(actionNoop());
53206 context.enter(mode);
53207 context.selectedNoteID(_note.id);
53210 function move(d3_event, entity, point) {
53211 d3_event.stopPropagation();
53212 _lastLoc = context.projection.invert(point);
53214 var nudge = geoViewportEdge(point, context.map().dimensions());
53217 startNudge(d3_event, nudge);
53223 function doMove(d3_event, nudge) {
53224 nudge = nudge || [0, 0];
53225 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
53226 var currMouse = geoVecSubtract(currPoint, nudge);
53227 var loc = context.projection.invert(currMouse);
53228 _note = _note.move(loc);
53229 var osm = services.osm;
53232 osm.replaceNote(_note); // update note cache
53235 context.replace(actionNoop()); // trigger redraw
53239 context.replace(actionNoop()); // trigger redraw
53241 context.selectedNoteID(_note.id).enter(modeSelectNote(context, _note.id));
53244 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);
53246 mode.enter = function () {
53247 context.install(edit);
53250 mode.exit = function () {
53251 context.ui().sidebar.hover.cancel();
53252 context.uninstall(edit);
53253 context.surface().selectAll('.active').classed('active', false);
53257 mode.behavior = drag;
53261 function uiDataHeader() {
53264 function dataHeader(selection) {
53265 var header = selection.selectAll('.data-header').data(_datum ? [_datum] : [], function (d) {
53266 return d.__featurehash__;
53268 header.exit().remove();
53269 var headerEnter = header.enter().append('div').attr('class', 'data-header');
53270 var iconEnter = headerEnter.append('div').attr('class', 'data-header-icon');
53271 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-data', 'note-fill'));
53272 headerEnter.append('div').attr('class', 'data-header-label').html(_t.html('map_data.layers.custom.title'));
53275 dataHeader.datum = function (val) {
53276 if (!arguments.length) return _datum;
53284 // It is keyed on the `value` of the entry. Data should be an array of objects like:
53286 // value: 'string value', // required
53287 // display: 'label html' // optional
53288 // title: 'hover text' // optional
53289 // terms: ['search terms'] // optional
53292 var _comboHideTimerID;
53294 function uiCombobox(context, klass) {
53295 var dispatch$1 = dispatch('accept', 'cancel');
53296 var container = context.container();
53297 var _suggestions = [];
53300 var _selected = null;
53301 var _canAutocomplete = true;
53302 var _caseSensitive = false;
53303 var _cancelFetch = false;
53307 var _mouseEnterHandler, _mouseLeaveHandler;
53309 var _fetcher = function _fetcher(val, cb) {
53310 cb(_data.filter(function (d) {
53311 var terms = d.terms || [];
53312 terms.push(d.value);
53313 return terms.some(function (term) {
53314 return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1;
53319 var combobox = function combobox(input, attachTo) {
53320 if (!input || input.empty()) return;
53321 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 () {
53322 var parent = this.parentNode;
53323 var sibling = this.nextSibling;
53324 select(parent).selectAll('.combobox-caret').filter(function (d) {
53325 return d === input.node();
53326 }).data([input.node()]).enter().insert('div', function () {
53328 }).attr('class', 'combobox-caret').on('mousedown.combo-caret', function (d3_event) {
53329 d3_event.preventDefault(); // don't steal focus from input
53331 input.node().focus(); // focus the input as if it was clicked
53333 mousedown(d3_event);
53334 }).on('mouseup.combo-caret', function (d3_event) {
53335 d3_event.preventDefault(); // don't steal focus from input
53341 function mousedown(d3_event) {
53342 if (d3_event.button !== 0) return; // left click only
53344 _tDown = +new Date(); // clear selection
53346 var start = input.property('selectionStart');
53347 var end = input.property('selectionEnd');
53349 if (start !== end) {
53350 var val = utilGetSetValue(input);
53351 input.node().setSelectionRange(val.length, val.length);
53355 input.on('mouseup.combo-input', mouseup);
53358 function mouseup(d3_event) {
53359 input.on('mouseup.combo-input', null);
53360 if (d3_event.button !== 0) return; // left click only
53362 if (input.node() !== document.activeElement) return; // exit if this input is not focused
53364 var start = input.property('selectionStart');
53365 var end = input.property('selectionEnd');
53366 if (start !== end) return; // exit if user is selecting
53367 // not showing or showing for a different field - try to show it.
53369 var combo = container.selectAll('.combobox');
53371 if (combo.empty() || combo.datum() !== input.node()) {
53372 var tOrig = _tDown;
53373 window.setTimeout(function () {
53374 if (tOrig !== _tDown) return; // exit if user double clicked
53376 fetchComboData('', function () {
53387 fetchComboData(''); // prefetch values (may warm taginfo cache)
53391 _comboHideTimerID = window.setTimeout(hide, 75);
53395 hide(); // remove any existing
53397 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) {
53398 // prevent moving focus out of the input field
53399 d3_event.preventDefault();
53401 container.on('scroll.combo-scroll', render, true);
53405 if (_comboHideTimerID) {
53406 window.clearTimeout(_comboHideTimerID);
53407 _comboHideTimerID = undefined;
53410 container.selectAll('.combobox').remove();
53411 container.on('scroll.combo-scroll', null);
53414 function keydown(d3_event) {
53415 var shown = !container.selectAll('.combobox').empty();
53416 var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
53418 switch (d3_event.keyCode) {
53419 case 8: // ⌫ Backspace
53423 d3_event.stopPropagation();
53426 input.on('input.combo-input', function () {
53427 var start = input.property('selectionStart');
53428 input.node().setSelectionRange(start, start);
53429 input.on('input.combo-input', change);
53440 d3_event.preventDefault();
53441 d3_event.stopPropagation();
53446 if (tagName === 'textarea' && !shown) return;
53447 d3_event.preventDefault();
53449 if (tagName === 'input' && !shown) {
53458 if (tagName === 'textarea' && !shown) return;
53459 d3_event.preventDefault();
53461 if (tagName === 'input' && !shown) {
53470 function keyup(d3_event) {
53471 switch (d3_event.keyCode) {
53482 } // Called whenever the input value is changed (e.g. on typing)
53485 function change() {
53486 fetchComboData(value(), function () {
53488 var val = input.property('value');
53490 if (_suggestions.length) {
53491 if (input.property('selectionEnd') === val.length) {
53492 _selected = tryAutocomplete();
53501 var combo = container.selectAll('.combobox');
53503 if (combo.empty()) {
53512 } // Called when the user presses up/down arrows to navigate the list
53515 function nav(dir) {
53516 if (_suggestions.length) {
53517 // try to determine previously selected index..
53520 for (var i = 0; i < _suggestions.length; i++) {
53521 if (_selected && _suggestions[i].value === _selected) {
53525 } // pick new _selected
53528 index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
53529 _selected = _suggestions[index].value;
53530 input.property('value', _selected);
53537 function ensureVisible() {
53538 var combo = container.selectAll('.combobox');
53539 if (combo.empty()) return;
53540 var containerRect = container.node().getBoundingClientRect();
53541 var comboRect = combo.node().getBoundingClientRect();
53543 if (comboRect.bottom > containerRect.bottom) {
53544 var node = attachTo ? attachTo.node() : input.node();
53545 node.scrollIntoView({
53546 behavior: 'instant',
53550 } // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
53553 var selected = combo.selectAll('.combobox-option.selected').node();
53556 selected.scrollIntoView({
53557 behavior: 'smooth',
53564 var value = input.property('value');
53565 var start = input.property('selectionStart');
53566 var end = input.property('selectionEnd');
53568 if (start && end) {
53569 value = value.substring(0, start);
53575 function fetchComboData(v, cb) {
53576 _cancelFetch = false;
53578 _fetcher.call(input, v, function (results) {
53579 // already chose a value, don't overwrite or autocomplete it
53580 if (_cancelFetch) return;
53581 _suggestions = results;
53582 results.forEach(function (d) {
53583 _fetched[d.value] = d;
53592 function tryAutocomplete() {
53593 if (!_canAutocomplete) return;
53594 var val = _caseSensitive ? value() : value().toLowerCase();
53595 if (!val) return; // Don't autocomplete if user is typing a number - #4935
53597 if (!isNaN(parseFloat(val)) && isFinite(val)) return;
53598 var bestIndex = -1;
53600 for (var i = 0; i < _suggestions.length; i++) {
53601 var suggestion = _suggestions[i].value;
53602 var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); // if search string matches suggestion exactly, pick it..
53604 if (compare === val) {
53606 break; // otherwise lock in the first result that starts with the search string..
53607 } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
53612 if (bestIndex !== -1) {
53613 var bestVal = _suggestions[bestIndex].value;
53614 input.property('value', bestVal);
53615 input.node().setSelectionRange(val.length, bestVal.length);
53620 function render() {
53621 if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
53626 var shown = !container.selectAll('.combobox').empty();
53627 if (!shown) return;
53628 var combo = container.selectAll('.combobox');
53629 var options = combo.selectAll('.combobox-option').data(_suggestions, function (d) {
53632 options.exit().remove(); // enter/update
53634 options.enter().append('a').attr('class', 'combobox-option').attr('title', function (d) {
53636 }).html(function (d) {
53637 return d.display || d.value;
53638 }).on('mouseenter', _mouseEnterHandler).on('mouseleave', _mouseLeaveHandler).merge(options).classed('selected', function (d) {
53639 return d.value === _selected;
53640 }).on('click.combo-option', accept).order();
53641 var node = attachTo ? attachTo.node() : input.node();
53642 var containerRect = container.node().getBoundingClientRect();
53643 var rect = node.getBoundingClientRect();
53644 combo.style('left', rect.left + 5 - containerRect.left + 'px').style('width', rect.width - 10 + 'px').style('top', rect.height + rect.top - containerRect.top + 'px');
53645 } // Dispatches an 'accept' event
53646 // Then hides the combobox.
53649 function accept(d3_event, d) {
53650 _cancelFetch = true;
53651 var thiz = input.node();
53654 // user clicked on a suggestion
53655 utilGetSetValue(input, d.value); // replace field contents
53657 utilTriggerEvent(input, 'change');
53658 } // clear (and keep) selection
53661 var val = utilGetSetValue(input);
53662 thiz.setSelectionRange(val.length, val.length);
53664 dispatch$1.call('accept', thiz, d, val);
53666 } // Dispatches an 'cancel' event
53667 // Then hides the combobox.
53670 function cancel() {
53671 _cancelFetch = true;
53672 var thiz = input.node(); // clear (and remove) selection, and replace field contents
53674 var val = utilGetSetValue(input);
53675 var start = input.property('selectionStart');
53676 var end = input.property('selectionEnd');
53677 val = val.slice(0, start) + val.slice(end);
53678 utilGetSetValue(input, val);
53679 thiz.setSelectionRange(val.length, val.length);
53680 dispatch$1.call('cancel', thiz);
53685 combobox.canAutocomplete = function (val) {
53686 if (!arguments.length) return _canAutocomplete;
53687 _canAutocomplete = val;
53691 combobox.caseSensitive = function (val) {
53692 if (!arguments.length) return _caseSensitive;
53693 _caseSensitive = val;
53697 combobox.data = function (val) {
53698 if (!arguments.length) return _data;
53703 combobox.fetcher = function (val) {
53704 if (!arguments.length) return _fetcher;
53709 combobox.minItems = function (val) {
53710 if (!arguments.length) return _minItems;
53715 combobox.itemsMouseEnter = function (val) {
53716 if (!arguments.length) return _mouseEnterHandler;
53717 _mouseEnterHandler = val;
53721 combobox.itemsMouseLeave = function (val) {
53722 if (!arguments.length) return _mouseLeaveHandler;
53723 _mouseLeaveHandler = val;
53727 return utilRebind(combobox, dispatch$1, 'on');
53730 uiCombobox.off = function (input, context) {
53731 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);
53732 context.container().on('scroll.combo-scroll', null);
53735 // hide class, which sets display=none, and a d3 transition for opacity.
53736 // this will cause blinking when called repeatedly, so check that the
53737 // value actually changes between calls.
53739 function uiToggle(show, callback) {
53740 return function (selection) {
53741 selection.style('opacity', show ? 0 : 1).classed('hide', false).transition().style('opacity', show ? 1 : 0).on('end', function () {
53742 select(this).classed('hide', !show).style('opacity', null);
53743 if (callback) callback.apply(this);
53748 function uiDisclosure(context, key, expandedDefault) {
53749 var dispatch$1 = dispatch('toggled');
53753 var _label = utilFunctor('');
53755 var _updatePreference = true;
53757 var _content = function _content() {};
53759 var disclosure = function disclosure(selection) {
53760 if (_expanded === undefined || _expanded === null) {
53761 // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
53762 var preference = corePreferences('disclosure.' + key + '.expanded');
53763 _expanded = preference === null ? !!expandedDefault : preference === 'true';
53766 var hideToggle = selection.selectAll('.hide-toggle-' + key).data([0]); // enter
53768 var hideToggleEnter = hideToggle.enter().append('a').attr('href', '#').attr('class', 'hide-toggle hide-toggle-' + key).call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
53769 hideToggleEnter.append('span').attr('class', 'hide-toggle-text'); // update
53771 hideToggle = hideToggleEnter.merge(hideToggle);
53772 hideToggle.on('click', toggle).classed('expanded', _expanded);
53773 hideToggle.selectAll('.hide-toggle-text').html(_label());
53774 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
53775 var wrap = selection.selectAll('.disclosure-wrap').data([0]); // enter/update
53777 wrap = wrap.enter().append('div').attr('class', 'disclosure-wrap disclosure-wrap-' + key).merge(wrap).classed('hide', !_expanded);
53780 wrap.call(_content);
53783 function toggle(d3_event) {
53784 d3_event.preventDefault();
53785 _expanded = !_expanded;
53787 if (_updatePreference) {
53788 corePreferences('disclosure.' + key + '.expanded', _expanded);
53791 hideToggle.classed('expanded', _expanded);
53792 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
53793 wrap.call(uiToggle(_expanded));
53796 wrap.call(_content);
53799 dispatch$1.call('toggled', this, _expanded);
53803 disclosure.label = function (val) {
53804 if (!arguments.length) return _label;
53805 _label = utilFunctor(val);
53809 disclosure.expanded = function (val) {
53810 if (!arguments.length) return _expanded;
53815 disclosure.updatePreference = function (val) {
53816 if (!arguments.length) return _updatePreference;
53817 _updatePreference = val;
53821 disclosure.content = function (val) {
53822 if (!arguments.length) return _content;
53827 return utilRebind(disclosure, dispatch$1, 'on');
53830 // Can be labeled and collapsible.
53832 function uiSection(id, context) {
53833 var _classes = utilFunctor('');
53835 var _shouldDisplay;
53843 var _expandedByDefault = utilFunctor(true);
53845 var _disclosureContent;
53847 var _disclosureExpanded;
53849 var _containerSelection = select(null);
53855 section.classes = function (val) {
53856 if (!arguments.length) return _classes;
53857 _classes = utilFunctor(val);
53861 section.label = function (val) {
53862 if (!arguments.length) return _label;
53863 _label = utilFunctor(val);
53867 section.expandedByDefault = function (val) {
53868 if (!arguments.length) return _expandedByDefault;
53869 _expandedByDefault = utilFunctor(val);
53873 section.shouldDisplay = function (val) {
53874 if (!arguments.length) return _shouldDisplay;
53875 _shouldDisplay = utilFunctor(val);
53879 section.content = function (val) {
53880 if (!arguments.length) return _content;
53885 section.disclosureContent = function (val) {
53886 if (!arguments.length) return _disclosureContent;
53887 _disclosureContent = val;
53891 section.disclosureExpanded = function (val) {
53892 if (!arguments.length) return _disclosureExpanded;
53893 _disclosureExpanded = val;
53895 }; // may be called multiple times
53898 section.render = function (selection) {
53899 _containerSelection = selection.selectAll('.section-' + id).data([0]);
53901 var sectionEnter = _containerSelection.enter().append('div').attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
53903 _containerSelection = sectionEnter.merge(_containerSelection);
53905 _containerSelection.call(renderContent);
53908 section.reRender = function () {
53909 _containerSelection.call(renderContent);
53912 section.selection = function () {
53913 return _containerSelection;
53916 section.disclosure = function () {
53917 return _disclosure;
53918 }; // may be called multiple times
53921 function renderContent(selection) {
53922 if (_shouldDisplay) {
53923 var shouldDisplay = _shouldDisplay();
53925 selection.classed('hide', !shouldDisplay);
53927 if (!shouldDisplay) {
53928 selection.html('');
53933 if (_disclosureContent) {
53934 if (!_disclosure) {
53935 _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()).label(_label || '')
53936 /*.on('toggled', function(expanded) {
53937 if (expanded) { selection.node().parentNode.scrollTop += 200; }
53939 .content(_disclosureContent);
53942 if (_disclosureExpanded !== undefined) {
53943 _disclosure.expanded(_disclosureExpanded);
53945 _disclosureExpanded = undefined;
53948 selection.call(_disclosure);
53953 selection.call(_content);
53961 // key: 'string', // required
53962 // value: 'string' // optional
53966 // qid: 'string' // brand wikidata (e.g. 'Q37158')
53970 function uiTagReference(what) {
53971 var wikibase = what.qid ? services.wikidata : services.osmWikibase;
53972 var tagReference = {};
53974 var _button = select(null);
53976 var _body = select(null);
53983 if (!wikibase) return;
53985 _button.classed('tag-reference-loading', true);
53987 wikibase.getDocs(what, gotDocs);
53990 function gotDocs(err, docs) {
53993 if (!docs || !docs.title) {
53994 _body.append('p').attr('class', 'tag-reference-description').html(_t.html('inspector.no_documentation_key'));
54000 if (docs.imageURL) {
54001 _body.append('img').attr('class', 'tag-reference-wiki-image').attr('src', docs.imageURL).on('load', function () {
54003 }).on('error', function () {
54004 select(this).remove();
54011 _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'));
54014 _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));
54015 } // Add link to info about "good changeset comments" - #2923
54018 if (what.key === 'comment') {
54019 _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'));
54026 _button.classed('tag-reference-loading', false);
54028 _body.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1');
54032 _button.selectAll('svg.icon use').each(function () {
54033 var iconUse = select(this);
54035 if (iconUse.attr('href') === '#iD-icon-info') {
54036 iconUse.attr('href', '#iD-icon-info-filled');
54042 _body.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
54043 _body.classed('expanded', false);
54048 _button.selectAll('svg.icon use').each(function () {
54049 var iconUse = select(this);
54051 if (iconUse.attr('href') === '#iD-icon-info-filled') {
54052 iconUse.attr('href', '#iD-icon-info');
54057 tagReference.button = function (selection, klass, iconName) {
54058 _button = selection.selectAll('.tag-reference-button').data([0]);
54059 _button = _button.enter().append('button').attr('class', 'tag-reference-button ' + (klass || '')).attr('title', _t('icons.information')).call(svgIcon('#iD-icon-' + (iconName || 'inspect'))).merge(_button);
54061 _button.on('click', function (d3_event) {
54062 d3_event.stopPropagation();
54063 d3_event.preventDefault();
54064 this.blur(); // avoid keeping focus on the button - #4641
54068 } else if (_loaded) {
54076 tagReference.body = function (selection) {
54077 var itemID = what.qid || what.key + '-' + (what.value || '');
54078 _body = selection.selectAll('.tag-reference-body').data([itemID], function (d) {
54082 _body.exit().remove();
54084 _body = _body.enter().append('div').attr('class', 'tag-reference-body').style('max-height', '0').style('opacity', '0').merge(_body);
54086 if (_showing === false) {
54091 tagReference.showing = function (val) {
54092 if (!arguments.length) return _showing;
54094 return tagReference;
54097 return tagReference;
54100 function uiSectionRawTagEditor(id, context) {
54101 var section = uiSection(id, context).classes('raw-tag-editor').label(function () {
54102 var count = Object.keys(_tags).filter(function (d) {
54105 return _t('inspector.title_count', {
54106 title: _t.html('inspector.tags'),
54109 }).expandedByDefault(false).disclosureContent(renderDisclosureContent);
54110 var taginfo = services.taginfo;
54111 var dispatch$1 = dispatch('change');
54112 var availableViews = [{
54114 icon: '#fas-th-list'
54117 icon: '#fas-i-cursor'
54120 var _tagView = corePreferences('raw-tag-editor-view') || 'list'; // 'list, 'text'
54123 var _readOnlyTags = []; // the keys in the order we want them to display
54125 var _orderedKeys = [];
54126 var _showBlank = false;
54127 var _pendingChange = null;
54137 var _didInteract = false;
54139 function interacted() {
54140 _didInteract = true;
54143 function renderDisclosureContent(wrap) {
54144 // remove deleted keys
54145 _orderedKeys = _orderedKeys.filter(function (key) {
54146 return _tags[key] !== undefined;
54147 }); // When switching to a different entity or changing the state (hover/select)
54148 // reorder the keys alphabetically.
54149 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
54150 // Otherwise leave their order alone - #5857, #5927
54152 var all = Object.keys(_tags).sort();
54153 var missingKeys = utilArrayDifference(all, _orderedKeys);
54155 for (var i in missingKeys) {
54156 _orderedKeys.push(missingKeys[i]);
54157 } // assemble row data
54160 var rowData = _orderedKeys.map(function (key, i) {
54166 }); // append blank row last, if necessary
54169 if (!rowData.length || _showBlank) {
54170 _showBlank = false;
54172 index: rowData.length,
54179 var options = wrap.selectAll('.raw-tag-options').data([0]);
54180 options.exit().remove();
54181 var optionsEnter = options.enter().insert('div', ':first-child').attr('class', 'raw-tag-options');
54182 var optionEnter = optionsEnter.selectAll('.raw-tag-option').data(availableViews, function (d) {
54185 optionEnter.append('button').attr('class', function (d) {
54186 return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
54187 }).attr('title', function (d) {
54188 return _t('icons.' + d.id);
54189 }).on('click', function (d3_event, d) {
54191 corePreferences('raw-tag-editor-view', d.id);
54192 wrap.selectAll('.raw-tag-option').classed('selected', function (datum) {
54193 return datum === d;
54195 wrap.selectAll('.tag-text').classed('hide', d.id !== 'text').each(setTextareaHeight);
54196 wrap.selectAll('.tag-list, .add-row').classed('hide', d.id !== 'list');
54197 }).each(function (d) {
54198 select(this).call(svgIcon(d.icon));
54199 }); // View as Text
54201 var textData = rowsToText(rowData);
54202 var textarea = wrap.selectAll('.tag-text').data([0]);
54203 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);
54204 textarea.call(utilGetSetValue, textData).each(setTextareaHeight).on('input', setTextareaHeight).on('focus', interacted).on('blur', textChanged).on('change', textChanged); // View as List
54206 var list = wrap.selectAll('.tag-list').data([0]);
54207 list = list.enter().append('ul').attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')).merge(list); // Container for the Add button
54209 var addRowEnter = wrap.selectAll('.add-row').data([0]).enter().append('div').attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
54210 addRowEnter.append('button').attr('class', 'add-tag').call(svgIcon('#iD-icon-plus', 'light')).on('click', addTag);
54211 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
54213 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
54216 var items = list.selectAll('.tag-row').data(rowData, function (d) {
54219 items.exit().each(unbind).remove(); // Enter
54221 var itemsEnter = items.enter().append('li').attr('class', 'tag-row').classed('readonly', isReadOnly);
54222 var innerWrap = itemsEnter.append('div').attr('class', 'inner-wrap');
54223 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);
54224 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);
54225 innerWrap.append('button').attr('class', 'form-field-button remove').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); // Update
54227 items = items.merge(itemsEnter).sort(function (a, b) {
54228 return a.index - b.index;
54230 items.each(function (d) {
54231 var row = select(this);
54232 var key = row.select('input.key'); // propagate bound data
54234 var value = row.select('input.value'); // propagate bound data
54236 if (_entityIDs && taginfo && _state !== 'hover') {
54237 bindTypeahead(key, value);
54240 var referenceOptions = {
54244 if (typeof d.value === 'string') {
54245 referenceOptions.value = d.value;
54248 var reference = uiTagReference(referenceOptions);
54250 if (_state === 'hover') {
54251 reference.showing(false);
54254 row.select('.inner-wrap') // propagate bound data
54255 .call(reference.button);
54256 row.call(reference.body);
54257 row.select('button.remove'); // propagate bound data
54259 items.selectAll('input.key').attr('title', function (d) {
54261 }).call(utilGetSetValue, function (d) {
54263 }).attr('readonly', function (d) {
54264 return isReadOnly(d) || typeof d.value !== 'string' || null;
54266 items.selectAll('input.value').attr('title', function (d) {
54267 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
54268 }).classed('mixed', function (d) {
54269 return Array.isArray(d.value);
54270 }).attr('placeholder', function (d) {
54271 return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
54272 }).call(utilGetSetValue, function (d) {
54273 return typeof d.value === 'string' ? d.value : '';
54274 }).attr('readonly', function (d) {
54275 return isReadOnly(d) || null;
54277 items.selectAll('button.remove').on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878
54280 function isReadOnly(d) {
54281 for (var i = 0; i < _readOnlyTags.length; i++) {
54282 if (d.key.match(_readOnlyTags[i]) !== null) {
54290 function setTextareaHeight() {
54291 if (_tagView !== 'text') return;
54292 var selection = select(this);
54293 var matches = selection.node().value.match(/\n/g);
54294 var lineCount = 2 + Number(matches && matches.length);
54295 var lineHeight = 20;
54296 selection.style('height', lineCount * lineHeight + 'px');
54299 function stringify(s) {
54300 return JSON.stringify(s).slice(1, -1); // without leading/trailing "
54303 function unstringify(s) {
54307 if (s.length < 1 || s.charAt(0) !== '"') {
54311 if (s.length < 2 || s.charAt(s.length - 1) !== '"' || s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') {
54315 return JSON.parse(leading + s + trailing);
54318 function rowsToText(rows) {
54319 var str = rows.filter(function (row) {
54320 return row.key && row.key.trim() !== '';
54321 }).map(function (row) {
54322 var rawVal = row.value;
54323 if (typeof rawVal !== 'string') rawVal = '*';
54324 var val = rawVal ? stringify(rawVal) : '';
54325 return stringify(row.key) + '=' + val;
54328 if (_state !== 'hover' && str.length) {
54335 function textChanged() {
54336 var newText = this.value.trim();
54338 newText.split('\n').forEach(function (row) {
54339 var m = row.match(/^\s*([^=]+)=(.*)$/);
54342 var k = context.cleanTagKey(unstringify(m[1].trim()));
54343 var v = context.cleanTagValue(unstringify(m[2].trim()));
54347 var tagDiff = utilTagDiff(_tags, newTags);
54348 if (!tagDiff.length) return;
54349 _pendingChange = _pendingChange || {};
54350 tagDiff.forEach(function (change) {
54353 })) return; // skip unchanged multiselection placeholders
54355 if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
54357 if (change.type === '-') {
54358 _pendingChange[change.key] = undefined;
54359 } else if (change.type === '+') {
54360 _pendingChange[change.key] = change.newVal || '';
54364 if (Object.keys(_pendingChange).length === 0) {
54365 _pendingChange = null;
54372 function pushMore(d3_event) {
54373 // if pressing Tab on the last value field with content, add a blank row
54374 if (d3_event.keyCode === 9 && !d3_event.shiftKey && section.selection().selectAll('.tag-list li:last-child input.value').node() === this && utilGetSetValue(select(this))) {
54379 function bindTypeahead(key, value) {
54380 if (isReadOnly(key.datum())) return;
54382 if (Array.isArray(value.datum().value)) {
54383 value.call(uiCombobox(context, 'tag-value').minItems(1).fetcher(function (value, callback) {
54384 var keyString = utilGetSetValue(key);
54385 if (!_tags[keyString]) return;
54387 var data = _tags[keyString].filter(Boolean).map(function (tagValue) {
54399 var geometry = context.graph().geometry(_entityIDs[0]);
54400 key.call(uiCombobox(context, 'tag-key').fetcher(function (value, callback) {
54403 geometry: geometry,
54405 }, function (err, data) {
54407 var filtered = data.filter(function (d) {
54408 return _tags[d.value] === undefined;
54410 callback(sort(value, filtered));
54414 value.call(uiCombobox(context, 'tag-value').fetcher(function (value, callback) {
54417 key: utilGetSetValue(key),
54418 geometry: geometry,
54420 }, function (err, data) {
54421 if (!err) callback(sort(value, data));
54425 function sort(value, data) {
54426 var sameletter = [];
54429 for (var i = 0; i < data.length; i++) {
54430 if (data[i].value.substring(0, value.length) === value) {
54431 sameletter.push(data[i]);
54433 other.push(data[i]);
54437 return sameletter.concat(other);
54441 function unbind() {
54442 var row = select(this);
54443 row.selectAll('input.key').call(uiCombobox.off, context);
54444 row.selectAll('input.value').call(uiCombobox.off, context);
54447 function keyChange(d3_event, d) {
54448 if (select(this).attr('readonly')) return;
54449 var kOld = d.key; // exit if we are currently about to delete this row anyway - #6366
54451 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
54452 var kNew = context.cleanTagKey(this.value.trim()); // allow no change if the key should be readonly
54461 if (kNew && kNew !== kOld && _tags[kNew] !== undefined) {
54462 // new key is already in use, switch focus to the existing row
54463 this.value = kOld; // reset the key
54465 section.selection().selectAll('.tag-list input.value').each(function (d) {
54466 if (d.key === kNew) {
54467 // send focus to that other value combo instead
54468 var input = select(this).node();
54476 var row = this.parentNode.parentNode;
54477 var inputVal = select(row).selectAll('input.value');
54478 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
54479 _pendingChange = _pendingChange || {};
54482 _pendingChange[kOld] = undefined;
54485 _pendingChange[kNew] = vNew; // update the ordered key index so this row doesn't change position
54487 var existingKeyIndex = _orderedKeys.indexOf(kOld);
54489 if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
54490 d.key = kNew; // update datum to avoid exit/enter on tag update
54494 utilGetSetValue(inputVal, vNew);
54498 function valueChange(d3_event, d) {
54499 if (isReadOnly(d)) return; // exit if this is a multiselection and no value was entered
54501 if (typeof d.value !== 'string' && !this.value) return; // exit if we are currently about to delete this row anyway - #6366
54503 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
54504 _pendingChange = _pendingChange || {};
54505 _pendingChange[d.key] = context.cleanTagValue(this.value);
54509 function removeTag(d3_event, d) {
54510 if (isReadOnly(d)) return;
54512 if (d.key === '') {
54513 // removing the blank row
54514 _showBlank = false;
54515 section.reRender();
54517 // remove the key from the ordered key index
54518 _orderedKeys = _orderedKeys.filter(function (key) {
54519 return key !== d.key;
54521 _pendingChange = _pendingChange || {};
54522 _pendingChange[d.key] = undefined;
54527 function addTag() {
54528 // Delay render in case this click is blurring an edited combo.
54529 // Without the setTimeout, the `content` render would wipe out the pending tag change.
54530 window.setTimeout(function () {
54532 section.reRender();
54533 section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
54537 function scheduleChange() {
54538 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
54539 var entityIDs = _entityIDs; // Delay change in case this change is blurring an edited combo. - #5878
54541 window.setTimeout(function () {
54542 if (!_pendingChange) return;
54543 dispatch$1.call('change', this, entityIDs, _pendingChange);
54544 _pendingChange = null;
54548 section.state = function (val) {
54549 if (!arguments.length) return _state;
54551 if (_state !== val) {
54559 section.presets = function (val) {
54560 if (!arguments.length) return _presets;
54563 if (_presets && _presets.length && _presets[0].isFallback()) {
54564 section.disclosureExpanded(true); // don't collapse the disclosure if the mapper used the raw tag editor - #1881
54565 } else if (!_didInteract) {
54566 section.disclosureExpanded(null);
54572 section.tags = function (val) {
54573 if (!arguments.length) return _tags;
54578 section.entityIDs = function (val) {
54579 if (!arguments.length) return _entityIDs;
54581 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
54587 }; // pass an array of regular expressions to test against the tag key
54590 section.readOnlyTags = function (val) {
54591 if (!arguments.length) return _readOnlyTags;
54592 _readOnlyTags = val;
54596 return utilRebind(section, dispatch$1, 'on');
54599 function uiDataEditor(context) {
54600 var dataHeader = uiDataHeader();
54601 var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context).expandedByDefault(true).readOnlyTags([/./]);
54605 function dataEditor(selection) {
54606 var header = selection.selectAll('.header').data([0]);
54607 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
54608 headerEnter.append('button').attr('class', 'close').on('click', function () {
54609 context.enter(modeBrowse(context));
54610 }).call(svgIcon('#iD-icon-close'));
54611 headerEnter.append('h3').html(_t.html('map_data.title'));
54612 var body = selection.selectAll('.body').data([0]);
54613 body = body.enter().append('div').attr('class', 'body').merge(body);
54614 var editor = body.selectAll('.data-editor').data([0]); // enter/update
54616 editor.enter().append('div').attr('class', 'modal-section data-editor').merge(editor).call(dataHeader.datum(_datum));
54617 var rte = body.selectAll('.raw-tag-editor').data([0]); // enter/update
54619 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);
54622 dataEditor.datum = function (val) {
54623 if (!arguments.length) return _datum;
54631 function modeSelectData(context, selectedDatum) {
54636 var keybinding = utilKeybinding('select-data');
54637 var dataEditor = uiDataEditor(context);
54638 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
54640 function selectData(d3_event, drawn) {
54641 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
54643 if (selection.empty()) {
54644 // Return to browse mode if selected DOM elements have
54645 // disappeared because the user moved them out of view..
54646 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
54648 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
54649 context.enter(modeBrowse(context));
54652 selection.classed('selected', true);
54657 if (context.container().select('.combobox').size()) return;
54658 context.enter(modeBrowse(context));
54661 mode.zoomToSelected = function () {
54662 var extent = geoExtent(d3_geoBounds(selectedDatum));
54663 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
54666 mode.enter = function () {
54667 behaviors.forEach(context.install);
54668 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
54669 select(document).call(keybinding);
54671 var sidebar = context.ui().sidebar;
54672 sidebar.show(dataEditor.datum(selectedDatum)); // expand the sidebar, avoid obscuring the data if needed
54674 var extent = geoExtent(d3_geoBounds(selectedDatum));
54675 sidebar.expand(sidebar.intersects(extent));
54676 context.map().on('drawn.select-data', selectData);
54679 mode.exit = function () {
54680 behaviors.forEach(context.uninstall);
54681 select(document).call(keybinding.unbind);
54682 context.surface().selectAll('.layer-mapdata .selected').classed('selected hover', false);
54683 context.map().on('drawn.select-data', null);
54684 context.ui().sidebar.hide();
54690 function uiImproveOsmComments() {
54693 function issueComments(selection) {
54694 // make the div immediately so it appears above the buttons
54695 var comments = selection.selectAll('.comments-container').data([0]);
54696 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); // must retrieve comments from API before they can be displayed
54698 services.improveOSM.getComments(_qaItem).then(function (d) {
54699 if (!d.comments) return; // nothing to do here
54701 var commentEnter = comments.selectAll('.comment').data(d.comments).enter().append('div').attr('class', 'comment');
54702 commentEnter.append('div').attr('class', 'comment-avatar').call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
54703 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
54704 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
54705 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
54706 var osm = services.osm;
54707 var selection = select(this);
54709 if (osm && d.username) {
54710 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.username)).attr('target', '_blank');
54713 selection.html(function (d) {
54717 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
54718 return _t.html('note.status.commented', {
54719 when: localeDateString(d.timestamp)
54722 mainEnter.append('div').attr('class', 'comment-text').append('p').html(function (d) {
54725 })["catch"](function (err) {
54726 console.log(err); // eslint-disable-line no-console
54730 function localeDateString(s) {
54731 if (!s) return null;
54737 var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
54739 if (isNaN(d.getTime())) return null;
54740 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
54743 issueComments.issue = function (val) {
54744 if (!arguments.length) return _qaItem;
54746 return issueComments;
54749 return issueComments;
54752 function uiImproveOsmDetails(context) {
54755 function issueDetail(d) {
54756 if (d.desc) return d.desc;
54757 var issueKey = d.issueKey;
54758 d.replacements = d.replacements || {};
54759 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
54761 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".description"), d.replacements);
54764 function improveOsmDetails(selection) {
54765 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
54766 return "".concat(d.id, "-").concat(d.status || 0);
54768 details.exit().remove();
54769 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
54771 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
54772 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
54773 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
54775 var relatedEntities = [];
54776 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
54777 var link = select(this);
54778 var isObjectLink = link.classed('error_object_link');
54779 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
54780 var entity = context.hasEntity(entityID);
54781 relatedEntities.push(entityID); // Add click handler
54783 link.on('mouseenter', function () {
54784 utilHighlightEntities([entityID], true, context);
54785 }).on('mouseleave', function () {
54786 utilHighlightEntities([entityID], false, context);
54787 }).on('click', function (d3_event) {
54788 d3_event.preventDefault();
54789 utilHighlightEntities([entityID], false, context);
54790 var osmlayer = context.layers().layer('osm');
54792 if (!osmlayer.enabled()) {
54793 osmlayer.enabled(true);
54796 context.map().centerZoom(_qaItem.loc, 20);
54799 context.enter(modeSelect(context, [entityID]));
54801 context.loadEntity(entityID, function () {
54802 context.enter(modeSelect(context, [entityID]));
54805 }); // Replace with friendly name if possible
54806 // (The entity may not yet be loaded into the graph)
54809 var name = utilDisplayName(entity); // try to use common name
54811 if (!name && !isObjectLink) {
54812 var preset = _mainPresetIndex.match(entity, context.graph());
54813 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
54817 this.innerText = name;
54820 }); // Don't hide entities related to this error - #5880
54822 context.features().forceVisible(relatedEntities);
54823 context.map().pan([0, 0]); // trigger a redraw
54826 improveOsmDetails.issue = function (val) {
54827 if (!arguments.length) return _qaItem;
54829 return improveOsmDetails;
54832 return improveOsmDetails;
54835 function uiImproveOsmHeader() {
54838 function issueTitle(d) {
54839 var issueKey = d.issueKey;
54840 d.replacements = d.replacements || {};
54841 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
54843 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".title"), d.replacements);
54846 function improveOsmHeader(selection) {
54847 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
54848 return "".concat(d.id, "-").concat(d.status || 0);
54850 header.exit().remove();
54851 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
54852 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
54854 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
54855 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
54857 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');
54858 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
54859 var picon = d.icon;
54864 var isMaki = /^maki-/.test(picon);
54865 return "#".concat(picon).concat(isMaki ? '-11' : '');
54868 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
54871 improveOsmHeader.issue = function (val) {
54872 if (!arguments.length) return _qaItem;
54874 return improveOsmHeader;
54877 return improveOsmHeader;
54880 function uiImproveOsmEditor(context) {
54881 var dispatch$1 = dispatch('change');
54882 var qaDetails = uiImproveOsmDetails(context);
54883 var qaComments = uiImproveOsmComments();
54884 var qaHeader = uiImproveOsmHeader();
54888 function improveOsmEditor(selection) {
54889 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
54890 headerEnter.append('button').attr('class', 'close').on('click', function () {
54891 return context.enter(modeBrowse(context));
54892 }).call(svgIcon('#iD-icon-close'));
54893 headerEnter.append('h3').html(_t.html('QA.improveOSM.title'));
54894 var body = selection.selectAll('.body').data([0]);
54895 body = body.enter().append('div').attr('class', 'body').merge(body);
54896 var editor = body.selectAll('.qa-editor').data([0]);
54897 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);
54900 function improveOsmSaveSection(selection) {
54901 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54903 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
54904 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
54905 return "".concat(d.id, "-").concat(d.status || 0);
54908 saveSection.exit().remove(); // enter
54910 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
54911 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('note.newComment'));
54912 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
54913 return d.newComment;
54914 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
54916 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
54918 function changeInput() {
54919 var input = select(this);
54920 var val = input.property('value').trim();
54924 } // store the unsaved comment with the issue itself
54927 _qaItem = _qaItem.update({
54930 var qaService = services.improveOSM;
54933 qaService.replaceItem(_qaItem);
54936 saveSection.call(qaSaveButtons);
54940 function qaSaveButtons(selection) {
54941 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54943 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
54944 return d.status + d.id;
54947 buttonSection.exit().remove(); // enter
54949 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
54950 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
54951 buttonEnter.append('button').attr('class', 'button close-button action');
54952 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
54954 buttonSection = buttonSection.merge(buttonEnter);
54955 buttonSection.select('.comment-button').attr('disabled', function (d) {
54956 return d.newComment ? null : true;
54957 }).on('click.comment', function (d3_event, d) {
54958 this.blur(); // avoid keeping focus on the button - #4641
54960 var qaService = services.improveOSM;
54963 qaService.postUpdate(d, function (err, item) {
54964 return dispatch$1.call('change', item);
54968 buttonSection.select('.close-button').html(function (d) {
54969 var andComment = d.newComment ? '_comment' : '';
54970 return _t.html("QA.keepRight.close".concat(andComment));
54971 }).on('click.close', function (d3_event, d) {
54972 this.blur(); // avoid keeping focus on the button - #4641
54974 var qaService = services.improveOSM;
54977 d.newStatus = 'SOLVED';
54978 qaService.postUpdate(d, function (err, item) {
54979 return dispatch$1.call('change', item);
54983 buttonSection.select('.ignore-button').html(function (d) {
54984 var andComment = d.newComment ? '_comment' : '';
54985 return _t.html("QA.keepRight.ignore".concat(andComment));
54986 }).on('click.ignore', function (d3_event, d) {
54987 this.blur(); // avoid keeping focus on the button - #4641
54989 var qaService = services.improveOSM;
54992 d.newStatus = 'INVALID';
54993 qaService.postUpdate(d, function (err, item) {
54994 return dispatch$1.call('change', item);
54998 } // NOTE: Don't change method name until UI v3 is merged
55001 improveOsmEditor.error = function (val) {
55002 if (!arguments.length) return _qaItem;
55004 return improveOsmEditor;
55007 return utilRebind(improveOsmEditor, dispatch$1, 'on');
55010 function uiKeepRightDetails(context) {
55013 function issueDetail(d) {
55014 var itemType = d.itemType,
55015 parentIssueType = d.parentIssueType;
55016 var unknown = _t.html('inspector.unknown');
55017 var replacements = d.replacements || {};
55018 replacements["default"] = unknown; // special key `default` works as a fallback string
55020 var detail = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".description"), replacements);
55022 if (detail === unknown) {
55023 detail = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".description"), replacements);
55029 function keepRightDetails(selection) {
55030 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
55031 return "".concat(d.id, "-").concat(d.status || 0);
55033 details.exit().remove();
55034 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
55036 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55037 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
55038 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
55040 var relatedEntities = [];
55041 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
55042 var link = select(this);
55043 var isObjectLink = link.classed('error_object_link');
55044 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
55045 var entity = context.hasEntity(entityID);
55046 relatedEntities.push(entityID); // Add click handler
55048 link.on('mouseenter', function () {
55049 utilHighlightEntities([entityID], true, context);
55050 }).on('mouseleave', function () {
55051 utilHighlightEntities([entityID], false, context);
55052 }).on('click', function (d3_event) {
55053 d3_event.preventDefault();
55054 utilHighlightEntities([entityID], false, context);
55055 var osmlayer = context.layers().layer('osm');
55057 if (!osmlayer.enabled()) {
55058 osmlayer.enabled(true);
55061 context.map().centerZoomEase(_qaItem.loc, 20);
55064 context.enter(modeSelect(context, [entityID]));
55066 context.loadEntity(entityID, function () {
55067 context.enter(modeSelect(context, [entityID]));
55070 }); // Replace with friendly name if possible
55071 // (The entity may not yet be loaded into the graph)
55074 var name = utilDisplayName(entity); // try to use common name
55076 if (!name && !isObjectLink) {
55077 var preset = _mainPresetIndex.match(entity, context.graph());
55078 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
55082 this.innerText = name;
55085 }); // Don't hide entities related to this issue - #5880
55087 context.features().forceVisible(relatedEntities);
55088 context.map().pan([0, 0]); // trigger a redraw
55091 keepRightDetails.issue = function (val) {
55092 if (!arguments.length) return _qaItem;
55094 return keepRightDetails;
55097 return keepRightDetails;
55100 function uiKeepRightHeader() {
55103 function issueTitle(d) {
55104 var itemType = d.itemType,
55105 parentIssueType = d.parentIssueType;
55106 var unknown = _t.html('inspector.unknown');
55107 var replacements = d.replacements || {};
55108 replacements["default"] = unknown; // special key `default` works as a fallback string
55110 var title = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".title"), replacements);
55112 if (title === unknown) {
55113 title = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".title"), replacements);
55119 function keepRightHeader(selection) {
55120 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
55121 return "".concat(d.id, "-").concat(d.status || 0);
55123 header.exit().remove();
55124 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
55125 var iconEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
55128 iconEnter.append('div').attr('class', function (d) {
55129 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
55130 }).call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
55131 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
55134 keepRightHeader.issue = function (val) {
55135 if (!arguments.length) return _qaItem;
55137 return keepRightHeader;
55140 return keepRightHeader;
55143 function uiViewOnKeepRight() {
55146 function viewOnKeepRight(selection) {
55149 if (services.keepRight && _qaItem instanceof QAItem) {
55150 url = services.keepRight.issueURL(_qaItem);
55153 var link = selection.selectAll('.view-on-keepRight').data(url ? [url] : []); // exit
55155 link.exit().remove(); // enter
55157 var linkEnter = link.enter().append('a').attr('class', 'view-on-keepRight').attr('target', '_blank').attr('rel', 'noopener') // security measure
55158 .attr('href', function (d) {
55160 }).call(svgIcon('#iD-icon-out-link', 'inline'));
55161 linkEnter.append('span').html(_t.html('inspector.view_on_keepRight'));
55164 viewOnKeepRight.what = function (val) {
55165 if (!arguments.length) return _qaItem;
55167 return viewOnKeepRight;
55170 return viewOnKeepRight;
55173 function uiKeepRightEditor(context) {
55174 var dispatch$1 = dispatch('change');
55175 var qaDetails = uiKeepRightDetails(context);
55176 var qaHeader = uiKeepRightHeader();
55180 function keepRightEditor(selection) {
55181 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
55182 headerEnter.append('button').attr('class', 'close').on('click', function () {
55183 return context.enter(modeBrowse(context));
55184 }).call(svgIcon('#iD-icon-close'));
55185 headerEnter.append('h3').html(_t.html('QA.keepRight.title'));
55186 var body = selection.selectAll('.body').data([0]);
55187 body = body.enter().append('div').attr('class', 'body').merge(body);
55188 var editor = body.selectAll('.qa-editor').data([0]);
55189 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(keepRightSaveSection);
55190 var footer = selection.selectAll('.footer').data([0]);
55191 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnKeepRight().what(_qaItem));
55194 function keepRightSaveSection(selection) {
55195 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55197 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
55198 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
55199 return "".concat(d.id, "-").concat(d.status || 0);
55202 saveSection.exit().remove(); // enter
55204 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
55205 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('QA.keepRight.comment'));
55206 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
55207 return d.newComment || d.comment;
55208 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
55210 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
55212 function changeInput() {
55213 var input = select(this);
55214 var val = input.property('value').trim();
55216 if (val === _qaItem.comment) {
55218 } // store the unsaved comment with the issue itself
55221 _qaItem = _qaItem.update({
55224 var qaService = services.keepRight;
55227 qaService.replaceItem(_qaItem); // update keepright cache
55230 saveSection.call(qaSaveButtons);
55234 function qaSaveButtons(selection) {
55235 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55237 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
55238 return d.status + d.id;
55241 buttonSection.exit().remove(); // enter
55243 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
55244 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
55245 buttonEnter.append('button').attr('class', 'button close-button action');
55246 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
55248 buttonSection = buttonSection.merge(buttonEnter);
55249 buttonSection.select('.comment-button') // select and propagate data
55250 .attr('disabled', function (d) {
55251 return d.newComment ? null : true;
55252 }).on('click.comment', function (d3_event, d) {
55253 this.blur(); // avoid keeping focus on the button - #4641
55255 var qaService = services.keepRight;
55258 qaService.postUpdate(d, function (err, item) {
55259 return dispatch$1.call('change', item);
55263 buttonSection.select('.close-button') // select and propagate data
55264 .html(function (d) {
55265 var andComment = d.newComment ? '_comment' : '';
55266 return _t.html("QA.keepRight.close".concat(andComment));
55267 }).on('click.close', function (d3_event, d) {
55268 this.blur(); // avoid keeping focus on the button - #4641
55270 var qaService = services.keepRight;
55273 d.newStatus = 'ignore_t'; // ignore temporarily (item fixed)
55275 qaService.postUpdate(d, function (err, item) {
55276 return dispatch$1.call('change', item);
55280 buttonSection.select('.ignore-button') // select and propagate data
55281 .html(function (d) {
55282 var andComment = d.newComment ? '_comment' : '';
55283 return _t.html("QA.keepRight.ignore".concat(andComment));
55284 }).on('click.ignore', function (d3_event, d) {
55285 this.blur(); // avoid keeping focus on the button - #4641
55287 var qaService = services.keepRight;
55290 d.newStatus = 'ignore'; // ignore permanently (false positive)
55292 qaService.postUpdate(d, function (err, item) {
55293 return dispatch$1.call('change', item);
55297 } // NOTE: Don't change method name until UI v3 is merged
55300 keepRightEditor.error = function (val) {
55301 if (!arguments.length) return _qaItem;
55303 return keepRightEditor;
55306 return utilRebind(keepRightEditor, dispatch$1, 'on');
55309 function uiOsmoseDetails(context) {
55312 function issueString(d, type) {
55313 if (!d) return ''; // Issue strings are cached from Osmose API
55315 var s = services.osmose.getStrings(d.itemType);
55316 return type in s ? s[type] : '';
55319 function osmoseDetails(selection) {
55320 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
55321 return "".concat(d.id, "-").concat(d.status || 0);
55323 details.exit().remove();
55324 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // Description
55326 if (issueString(_qaItem, 'detail')) {
55327 var div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55328 div.append('h4').html(_t.html('QA.keepRight.detail_description'));
55329 div.append('p').attr('class', 'qa-details-description-text').html(function (d) {
55330 return issueString(d, 'detail');
55331 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55332 } // Elements (populated later as data is requested)
55335 var detailsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55336 var elemsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); // Suggested Fix (mustn't exist for every issue type)
55338 if (issueString(_qaItem, 'fix')) {
55339 var _div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55341 _div.append('h4').html(_t.html('QA.osmose.fix_title'));
55343 _div.append('p').html(function (d) {
55344 return issueString(d, 'fix');
55345 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55346 } // Common Pitfalls (mustn't exist for every issue type)
55349 if (issueString(_qaItem, 'trap')) {
55350 var _div2 = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55352 _div2.append('h4').html(_t.html('QA.osmose.trap_title'));
55354 _div2.append('p').html(function (d) {
55355 return issueString(d, 'trap');
55356 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55357 } // Save current item to check if UI changed by time request resolves
55360 var thisItem = _qaItem;
55361 services.osmose.loadIssueDetail(_qaItem).then(function (d) {
55362 // No details to add if there are no associated issue elements
55363 if (!d.elems || d.elems.length === 0) return; // Do nothing if UI has moved on by the time this resolves
55365 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
55368 detailsDiv.append('h4').html(_t.html('QA.osmose.detail_title'));
55369 detailsDiv.append('p').html(function (d) {
55371 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55372 } // Create list of linked issue elements
55375 elemsDiv.append('h4').html(_t.html('QA.osmose.elems_title'));
55376 elemsDiv.append('ul').selectAll('li').data(d.elems).enter().append('li').append('a').attr('href', '#').attr('class', 'error_entity_link').html(function (d) {
55378 }).each(function () {
55379 var link = select(this);
55380 var entityID = this.textContent;
55381 var entity = context.hasEntity(entityID); // Add click handler
55383 link.on('mouseenter', function () {
55384 utilHighlightEntities([entityID], true, context);
55385 }).on('mouseleave', function () {
55386 utilHighlightEntities([entityID], false, context);
55387 }).on('click', function (d3_event) {
55388 d3_event.preventDefault();
55389 utilHighlightEntities([entityID], false, context);
55390 var osmlayer = context.layers().layer('osm');
55392 if (!osmlayer.enabled()) {
55393 osmlayer.enabled(true);
55396 context.map().centerZoom(d.loc, 20);
55399 context.enter(modeSelect(context, [entityID]));
55401 context.loadEntity(entityID, function () {
55402 context.enter(modeSelect(context, [entityID]));
55405 }); // Replace with friendly name if possible
55406 // (The entity may not yet be loaded into the graph)
55409 var name = utilDisplayName(entity); // try to use common name
55412 var preset = _mainPresetIndex.match(entity, context.graph());
55413 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
55417 this.innerText = name;
55420 }); // Don't hide entities related to this issue - #5880
55422 context.features().forceVisible(d.elems);
55423 context.map().pan([0, 0]); // trigger a redraw
55424 })["catch"](function (err) {
55425 console.log(err); // eslint-disable-line no-console
55429 osmoseDetails.issue = function (val) {
55430 if (!arguments.length) return _qaItem;
55432 return osmoseDetails;
55435 return osmoseDetails;
55438 function uiOsmoseHeader() {
55441 function issueTitle(d) {
55442 var unknown = _t('inspector.unknown');
55443 if (!d) return unknown; // Issue titles supplied by Osmose
55445 var s = services.osmose.getStrings(d.itemType);
55446 return 'title' in s ? s.title : unknown;
55449 function osmoseHeader(selection) {
55450 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
55451 return "".concat(d.id, "-").concat(d.status || 0);
55453 header.exit().remove();
55454 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
55455 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
55457 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
55458 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
55460 svgEnter.append('polygon').attr('fill', function (d) {
55461 return services.osmose.getColor(d.item);
55462 }).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');
55463 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
55464 var picon = d.icon;
55469 var isMaki = /^maki-/.test(picon);
55470 return "#".concat(picon).concat(isMaki ? '-11' : '');
55473 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
55476 osmoseHeader.issue = function (val) {
55477 if (!arguments.length) return _qaItem;
55479 return osmoseHeader;
55482 return osmoseHeader;
55485 function uiViewOnOsmose() {
55488 function viewOnOsmose(selection) {
55491 if (services.osmose && _qaItem instanceof QAItem) {
55492 url = services.osmose.itemURL(_qaItem);
55495 var link = selection.selectAll('.view-on-osmose').data(url ? [url] : []); // exit
55497 link.exit().remove(); // enter
55499 var linkEnter = link.enter().append('a').attr('class', 'view-on-osmose').attr('target', '_blank').attr('rel', 'noopener') // security measure
55500 .attr('href', function (d) {
55502 }).call(svgIcon('#iD-icon-out-link', 'inline'));
55503 linkEnter.append('span').html(_t.html('inspector.view_on_osmose'));
55506 viewOnOsmose.what = function (val) {
55507 if (!arguments.length) return _qaItem;
55509 return viewOnOsmose;
55512 return viewOnOsmose;
55515 function uiOsmoseEditor(context) {
55516 var dispatch$1 = dispatch('change');
55517 var qaDetails = uiOsmoseDetails(context);
55518 var qaHeader = uiOsmoseHeader();
55522 function osmoseEditor(selection) {
55523 var header = selection.selectAll('.header').data([0]);
55524 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
55525 headerEnter.append('button').attr('class', 'close').on('click', function () {
55526 return context.enter(modeBrowse(context));
55527 }).call(svgIcon('#iD-icon-close'));
55528 headerEnter.append('h3').html(_t.html('QA.osmose.title'));
55529 var body = selection.selectAll('.body').data([0]);
55530 body = body.enter().append('div').attr('class', 'body').merge(body);
55531 var editor = body.selectAll('.qa-editor').data([0]);
55532 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(osmoseSaveSection);
55533 var footer = selection.selectAll('.footer').data([0]);
55534 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOsmose().what(_qaItem));
55537 function osmoseSaveSection(selection) {
55538 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55540 var isShown = _qaItem && isSelected;
55541 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
55542 return "".concat(d.id, "-").concat(d.status || 0);
55545 saveSection.exit().remove(); // enter
55547 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); // update
55549 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
55552 function qaSaveButtons(selection) {
55553 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55555 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
55556 return d.status + d.id;
55559 buttonSection.exit().remove(); // enter
55561 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
55562 buttonEnter.append('button').attr('class', 'button close-button action');
55563 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
55565 buttonSection = buttonSection.merge(buttonEnter);
55566 buttonSection.select('.close-button').html(_t.html('QA.keepRight.close')).on('click.close', function (d3_event, d) {
55567 this.blur(); // avoid keeping focus on the button - #4641
55569 var qaService = services.osmose;
55572 d.newStatus = 'done';
55573 qaService.postUpdate(d, function (err, item) {
55574 return dispatch$1.call('change', item);
55578 buttonSection.select('.ignore-button').html(_t.html('QA.keepRight.ignore')).on('click.ignore', function (d3_event, d) {
55579 this.blur(); // avoid keeping focus on the button - #4641
55581 var qaService = services.osmose;
55584 d.newStatus = 'false';
55585 qaService.postUpdate(d, function (err, item) {
55586 return dispatch$1.call('change', item);
55590 } // NOTE: Don't change method name until UI v3 is merged
55593 osmoseEditor.error = function (val) {
55594 if (!arguments.length) return _qaItem;
55596 return osmoseEditor;
55599 return utilRebind(osmoseEditor, dispatch$1, 'on');
55602 function modeSelectError(context, selectedErrorID, selectedErrorService) {
55604 id: 'select-error',
55607 var keybinding = utilKeybinding('select-error');
55608 var errorService = services[selectedErrorService];
55611 switch (selectedErrorService) {
55613 errorEditor = uiImproveOsmEditor(context).on('change', function () {
55614 context.map().pan([0, 0]); // trigger a redraw
55616 var error = checkSelectedID();
55617 if (!error) return;
55618 context.ui().sidebar.show(errorEditor.error(error));
55623 errorEditor = uiKeepRightEditor(context).on('change', function () {
55624 context.map().pan([0, 0]); // trigger a redraw
55626 var error = checkSelectedID();
55627 if (!error) return;
55628 context.ui().sidebar.show(errorEditor.error(error));
55633 errorEditor = uiOsmoseEditor(context).on('change', function () {
55634 context.map().pan([0, 0]); // trigger a redraw
55636 var error = checkSelectedID();
55637 if (!error) return;
55638 context.ui().sidebar.show(errorEditor.error(error));
55643 var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
55645 function checkSelectedID() {
55646 if (!errorService) return;
55647 var error = errorService.getError(selectedErrorID);
55650 context.enter(modeBrowse(context));
55656 mode.zoomToSelected = function () {
55657 if (!errorService) return;
55658 var error = errorService.getError(selectedErrorID);
55661 context.map().centerZoomEase(error.loc, 20);
55665 mode.enter = function () {
55666 var error = checkSelectedID();
55667 if (!error) return;
55668 behaviors.forEach(context.install);
55669 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
55670 select(document).call(keybinding);
55672 var sidebar = context.ui().sidebar;
55673 sidebar.show(errorEditor.error(error));
55674 context.map().on('drawn.select-error', selectError); // class the error as selected, or return to browse mode if the error is gone
55676 function selectError(d3_event, drawn) {
55677 if (!checkSelectedID()) return;
55678 var selection = context.surface().selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
55680 if (selection.empty()) {
55681 // Return to browse mode if selected DOM elements have
55682 // disappeared because the user moved them out of view..
55683 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
55685 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
55686 context.enter(modeBrowse(context));
55689 selection.classed('selected', true);
55690 context.selectedErrorID(selectedErrorID);
55695 if (context.container().select('.combobox').size()) return;
55696 context.enter(modeBrowse(context));
55700 mode.exit = function () {
55701 behaviors.forEach(context.uninstall);
55702 select(document).call(keybinding.unbind);
55703 context.surface().selectAll('.qaItem.selected').classed('selected hover', false);
55704 context.map().on('drawn.select-error', null);
55705 context.ui().sidebar.hide();
55706 context.selectedErrorID(null);
55707 context.features().forceVisible([]);
55713 function behaviorSelect(context) {
55714 var _tolerancePx = 4; // see also behaviorDrag
55716 var _lastMouseEvent = null;
55717 var _showMenu = false;
55718 var _downPointers = {};
55719 var _longPressTimeout = null;
55720 var _lastInteractionType = null; // the id of the down pointer that's enabling multiselection while down
55722 var _multiselectionPointerId = null; // use pointer events on supported platforms; fallback to mouse events
55724 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55726 function keydown(d3_event) {
55727 if (d3_event.keyCode === 32) {
55728 // don't react to spacebar events during text input
55729 var activeNode = document.activeElement;
55730 if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
55733 if (d3_event.keyCode === 93 || // context menu key
55734 d3_event.keyCode === 32) {
55736 d3_event.preventDefault();
55739 if (d3_event.repeat) return; // ignore repeated events for held keys
55740 // if any key is pressed the user is probably doing something other than long-pressing
55744 if (d3_event.shiftKey) {
55745 context.surface().classed('behavior-multiselect', true);
55748 if (d3_event.keyCode === 32) {
55750 if (!_downPointers.spacebar && _lastMouseEvent) {
55752 _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
55753 _downPointers.spacebar = {
55754 firstEvent: _lastMouseEvent,
55755 lastEvent: _lastMouseEvent
55761 function keyup(d3_event) {
55764 if (!d3_event.shiftKey) {
55765 context.surface().classed('behavior-multiselect', false);
55768 if (d3_event.keyCode === 93) {
55769 // context menu key
55770 d3_event.preventDefault();
55771 _lastInteractionType = 'menukey';
55772 contextmenu(d3_event);
55773 } else if (d3_event.keyCode === 32) {
55775 var pointer = _downPointers.spacebar;
55778 delete _downPointers.spacebar;
55779 if (pointer.done) return;
55780 d3_event.preventDefault();
55781 _lastInteractionType = 'spacebar';
55782 click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
55787 function pointerdown(d3_event) {
55788 var id = (d3_event.pointerId || 'mouse').toString();
55790 if (d3_event.buttons && d3_event.buttons !== 1) return;
55791 context.ui().closeEditMenu();
55792 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (d3_event.pointerType || 'mouse'));
55793 _downPointers[id] = {
55794 firstEvent: d3_event,
55795 lastEvent: d3_event
55799 function didLongPress(id, interactionType) {
55800 var pointer = _downPointers[id];
55801 if (!pointer) return;
55803 for (var i in _downPointers) {
55804 // don't allow this or any currently down pointer to trigger another click
55805 _downPointers[i].done = true;
55806 } // treat long presses like right-clicks
55809 _longPressTimeout = null;
55810 _lastInteractionType = interactionType;
55812 click(pointer.firstEvent, pointer.lastEvent, id);
55815 function pointermove(d3_event) {
55816 var id = (d3_event.pointerId || 'mouse').toString();
55818 if (_downPointers[id]) {
55819 _downPointers[id].lastEvent = d3_event;
55822 if (!d3_event.pointerType || d3_event.pointerType === 'mouse') {
55823 _lastMouseEvent = d3_event;
55825 if (_downPointers.spacebar) {
55826 _downPointers.spacebar.lastEvent = d3_event;
55831 function pointerup(d3_event) {
55832 var id = (d3_event.pointerId || 'mouse').toString();
55833 var pointer = _downPointers[id];
55834 if (!pointer) return;
55835 delete _downPointers[id];
55837 if (_multiselectionPointerId === id) {
55838 _multiselectionPointerId = null;
55841 if (pointer.done) return;
55842 click(pointer.firstEvent, d3_event, id);
55845 function pointercancel(d3_event) {
55846 var id = (d3_event.pointerId || 'mouse').toString();
55847 if (!_downPointers[id]) return;
55848 delete _downPointers[id];
55850 if (_multiselectionPointerId === id) {
55851 _multiselectionPointerId = null;
55855 function contextmenu(d3_event) {
55856 d3_event.preventDefault();
55858 if (!+d3_event.clientX && !+d3_event.clientY) {
55859 if (_lastMouseEvent) {
55860 d3_event.sourceEvent = _lastMouseEvent;
55865 _lastMouseEvent = d3_event;
55866 _lastInteractionType = 'rightclick';
55870 click(d3_event, d3_event);
55873 function click(firstEvent, lastEvent, pointerId) {
55875 var mapNode = context.container().select('.main-map').node(); // Use the `main-map` coordinate system since the surface and supersurface
55876 // are transformed when drag-panning.
55878 var pointGetter = utilFastMouse(mapNode);
55879 var p1 = pointGetter(firstEvent);
55880 var p2 = pointGetter(lastEvent);
55881 var dist = geoVecLength(p1, p2);
55883 if (dist > _tolerancePx || !mapContains(lastEvent)) {
55888 var targetDatum = lastEvent.target.__data__;
55889 var multiselectEntityId;
55891 if (!_multiselectionPointerId) {
55892 // If a different pointer than the one triggering this click is down on a
55893 // feature, treat this and all future clicks as multiselection until that
55894 // pointer is raised.
55895 var selectPointerInfo = pointerDownOnSelection(pointerId);
55897 if (selectPointerInfo) {
55898 _multiselectionPointerId = selectPointerInfo.pointerId; // if the other feature isn't selected yet, make sure we select it
55900 multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
55901 _downPointers[selectPointerInfo.pointerId].done = true;
55903 } // support multiselect if data is already selected
55906 var isMultiselect = context.mode().id === 'select' && ( // and shift key is down
55907 lastEvent && lastEvent.shiftKey || // or we're lasso-selecting
55908 context.surface().select('.lasso').node() || // or a pointer is down over a selected feature
55909 _multiselectionPointerId && !multiselectEntityId);
55911 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
55913 function mapContains(event) {
55914 var rect = mapNode.getBoundingClientRect();
55915 return event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;
55918 function pointerDownOnSelection(skipPointerId) {
55919 var mode = context.mode();
55920 var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
55922 for (var pointerId in _downPointers) {
55923 if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
55924 var pointerInfo = _downPointers[pointerId];
55925 var p1 = pointGetter(pointerInfo.firstEvent);
55926 var p2 = pointGetter(pointerInfo.lastEvent);
55927 if (geoVecLength(p1, p2) > _tolerancePx) continue;
55928 var datum = pointerInfo.firstEvent.target.__data__;
55929 var entity = datum && datum.properties && datum.properties.entity || datum;
55930 if (context.graph().hasEntity(entity.id)) return {
55931 pointerId: pointerId,
55932 entityId: entity.id,
55933 selected: selectedIDs.indexOf(entity.id) !== -1
55941 function processClick(datum, isMultiselect, point, alsoSelectId) {
55942 var mode = context.mode();
55943 var showMenu = _showMenu;
55944 var interactionType = _lastInteractionType;
55945 var entity = datum && datum.properties && datum.properties.entity;
55946 if (entity) datum = entity;
55948 if (datum && datum.type === 'midpoint') {
55949 // treat targeting midpoints as if targeting the parent way
55950 datum = datum.parents[0];
55955 if (datum instanceof osmEntity) {
55956 // targeting an entity
55957 var selectedIDs = context.selectedIDs();
55958 context.selectedNoteID(null);
55959 context.selectedErrorID(null);
55961 if (!isMultiselect) {
55962 // don't change the selection if we're toggling the menu atop a multiselection
55963 if (!showMenu || selectedIDs.length <= 1 || selectedIDs.indexOf(datum.id) === -1) {
55964 if (alsoSelectId === datum.id) alsoSelectId = null;
55965 selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]); // always enter modeSelect even if the entity is already
55966 // selected since listeners may expect `context.enter` events,
55967 // e.g. in the walkthrough
55969 newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
55970 context.enter(newMode);
55973 if (selectedIDs.indexOf(datum.id) !== -1) {
55974 // clicked entity is already in the selectedIDs list..
55976 // deselect clicked entity, then reenter select mode or return to browse mode..
55977 selectedIDs = selectedIDs.filter(function (id) {
55978 return id !== datum.id;
55980 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
55981 context.enter(newMode);
55984 // clicked entity is not in the selected list, add it..
55985 selectedIDs = selectedIDs.concat([datum.id]);
55986 newMode = mode.selectedIDs(selectedIDs);
55987 context.enter(newMode);
55990 } else if (datum && datum.__featurehash__ && !isMultiselect) {
55991 // targeting custom data
55992 context.selectedNoteID(null).enter(modeSelectData(context, datum));
55993 } else if (datum instanceof osmNote && !isMultiselect) {
55994 // targeting a note
55995 context.selectedNoteID(datum.id).enter(modeSelectNote(context, datum.id));
55996 } else if (datum instanceof QAItem & !isMultiselect) {
55997 // targeting an external QA issue
55998 context.selectedErrorID(datum.id).enter(modeSelectError(context, datum.id, datum.service));
56000 // targeting nothing
56001 context.selectedNoteID(null);
56002 context.selectedErrorID(null);
56004 if (!isMultiselect && mode.id !== 'browse') {
56005 context.enter(modeBrowse(context));
56009 context.ui().closeEditMenu(); // always request to show the edit menu in case the mode needs it
56011 if (showMenu) context.ui().showEditMenu(point, interactionType);
56015 function cancelLongPress() {
56016 if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
56017 _longPressTimeout = null;
56020 function resetProperties() {
56023 _lastInteractionType = null; // don't reset _lastMouseEvent since it might still be useful
56026 function behavior(selection) {
56028 _lastMouseEvent = context.map().lastPointerEvent();
56029 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) {
56030 // Edge and IE really like to show the contextmenu on the
56031 // menubar when user presses a keyboard menu button
56032 // even after we've already preventdefaulted the key event.
56035 if (+e.clientX === 0 && +e.clientY === 0) {
56036 d3_event.preventDefault();
56039 selection.on(_pointerPrefix + 'down.select', pointerdown).on('contextmenu.select', contextmenu);
56040 /*if (d3_event && d3_event.shiftKey) {
56042 .classed('behavior-multiselect', true);
56046 behavior.off = function (selection) {
56048 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);
56049 selection.on(_pointerPrefix + 'down.select', null).on('contextmenu.select', null);
56050 context.surface().classed('behavior-multiselect', false);
56056 function behaviorDrawWay(context, wayID, mode, startGraph) {
56057 var dispatch$1 = dispatch('rejectedSelfIntersection');
56058 var behavior = behaviorDraw(context); // Must be set by `drawWay.nodeIndex` before each install of this behavior.
56070 var _pointerHasMoved = false; // The osmNode to be placed.
56071 // This is temporary and just follows the mouse cursor until an "add" event occurs.
56075 var _didResolveTempEdit = false;
56077 function createDrawNode(loc) {
56078 // don't make the draw node until we actually need it
56079 _drawNode = osmNode({
56082 context.pauseChangeDispatch();
56083 context.replace(function actionAddDrawNode(graph) {
56084 // add the draw node to the graph and insert it into the way
56085 var way = graph.entity(wayID);
56086 return graph.replace(_drawNode).replace(way.addNode(_drawNode.id, _nodeIndex));
56088 context.resumeChangeDispatch();
56089 setActiveElements();
56092 function removeDrawNode() {
56093 context.pauseChangeDispatch();
56094 context.replace(function actionDeleteDrawNode(graph) {
56095 var way = graph.entity(wayID);
56096 return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
56098 _drawNode = undefined;
56099 context.resumeChangeDispatch();
56102 function keydown(d3_event) {
56103 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56104 if (context.surface().classed('nope')) {
56105 context.surface().classed('nope-suppressed', true);
56108 context.surface().classed('nope', false).classed('nope-disabled', true);
56112 function keyup(d3_event) {
56113 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56114 if (context.surface().classed('nope-suppressed')) {
56115 context.surface().classed('nope', true);
56118 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
56122 function allowsVertex(d) {
56123 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
56125 // - `mode/drag_node.js` `doMove()`
56126 // - `behavior/draw.js` `click()`
56127 // - `behavior/draw_way.js` `move()`
56130 function move(d3_event, datum) {
56131 var loc = context.map().mouseCoordinates();
56132 if (!_drawNode) createDrawNode(loc);
56133 context.surface().classed('nope-disabled', d3_event.altKey);
56134 var targetLoc = datum && datum.properties && datum.properties.entity && allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
56135 var targetNodes = datum && datum.properties && datum.properties.nodes;
56138 // snap to node/vertex - a point target with `.loc`
56140 } else if (targetNodes) {
56141 // snap to way - a line target with `.nodes`
56142 var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
56149 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56150 _drawNode = context.entity(_drawNode.id);
56152 /* includeDrawNode */
56154 } // Check whether this edit causes the geometry to break.
56155 // If so, class the surface with a nope cursor.
56156 // `includeDrawNode` - Only check the relevant line segments if finishing drawing
56159 function checkGeometry(includeDrawNode) {
56160 var nopeDisabled = context.surface().classed('nope-disabled');
56161 var isInvalid = isInvalidGeometry(includeDrawNode);
56163 if (nopeDisabled) {
56164 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
56166 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
56170 function isInvalidGeometry(includeDrawNode) {
56171 var testNode = _drawNode; // we only need to test the single way we're drawing
56173 var parentWay = context.graph().entity(wayID);
56174 var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy
56176 if (includeDrawNode) {
56177 if (parentWay.isClosed()) {
56178 // don't test the last segment for closed ways - #4655
56179 // (still test the first segment)
56183 // discount the draw node
56184 if (parentWay.isClosed()) {
56185 if (nodes.length < 3) return false;
56186 if (_drawNode) nodes.splice(-2, 1);
56187 testNode = nodes[nodes.length - 2];
56189 // there's nothing we need to test if we ignore the draw node on open ways
56194 return testNode && geoHasSelfIntersections(nodes, testNode.id);
56197 function undone() {
56198 // undoing removed the temp edit
56199 _didResolveTempEdit = true;
56200 context.pauseChangeDispatch();
56203 if (context.graph() === startGraph) {
56204 // We've undone back to the initial state before we started drawing.
56205 // Just exit the draw mode without undoing whatever we did before
56206 // we entered the draw mode.
56207 nextMode = modeSelect(context, [wayID]);
56209 // The `undo` only removed the temporary edit, so here we have to
56210 // manually undo to actually remove the last node we added. We can't
56211 // use the `undo` function since the initial "add" graph doesn't have
56212 // an annotation and so cannot be undone to.
56213 context.pop(1); // continue drawing
56216 } // clear the redo stack by adding and removing a blank edit
56219 context.perform(actionNoop());
56221 context.resumeChangeDispatch();
56222 context.enter(nextMode);
56225 function setActiveElements() {
56226 if (!_drawNode) return;
56227 context.surface().selectAll('.' + _drawNode.id).classed('active', true);
56230 function resetToStartGraph() {
56231 while (context.graph() !== startGraph) {
56236 var drawWay = function drawWay(surface) {
56237 _drawNode = undefined;
56238 _didResolveTempEdit = false;
56239 _origWay = context.entity(wayID);
56240 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] : _origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1];
56241 _wayGeometry = _origWay.geometry(context.graph());
56242 _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry);
56243 _pointerHasMoved = false; // Push an annotated state for undo to return back to.
56244 // We must make sure to replace or remove it later.
56246 context.pauseChangeDispatch();
56247 context.perform(actionNoop(), _annotation);
56248 context.resumeChangeDispatch();
56249 behavior.hover().initialNodeID(_headNodeID);
56250 behavior.on('move', function () {
56251 _pointerHasMoved = true;
56252 move.apply(this, arguments);
56253 }).on('down', function () {
56254 move.apply(this, arguments);
56255 }).on('downcancel', function () {
56256 if (_drawNode) removeDrawNode();
56257 }).on('click', drawWay.add).on('clickWay', drawWay.addWay).on('clickNode', drawWay.addNode).on('undo', context.undo).on('cancel', drawWay.cancel).on('finish', drawWay.finish);
56258 select(window).on('keydown.drawWay', keydown).on('keyup.drawWay', keyup);
56259 context.map().dblclickZoomEnable(false).on('drawn.draw', setActiveElements);
56260 setActiveElements();
56261 surface.call(behavior);
56262 context.history().on('undone.draw', undone);
56265 drawWay.off = function (surface) {
56266 if (!_didResolveTempEdit) {
56267 // Drawing was interrupted unexpectedly.
56268 // This can happen if the user changes modes,
56269 // clicks geolocate button, a hashchange event occurs, etc.
56270 context.pauseChangeDispatch();
56271 resetToStartGraph();
56272 context.resumeChangeDispatch();
56275 _drawNode = undefined;
56276 _nodeIndex = undefined;
56277 context.map().on('drawn.draw', null);
56278 surface.call(behavior.off).selectAll('.active').classed('active', false);
56279 surface.classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false);
56280 select(window).on('keydown.drawWay', null).on('keyup.drawWay', null);
56281 context.history().on('undone.draw', null);
56284 function attemptAdd(d, loc, doAdd) {
56286 // move the node to the final loc in case move wasn't called
56287 // consistently (e.g. on touch devices)
56288 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56289 _drawNode = context.entity(_drawNode.id);
56291 createDrawNode(loc);
56295 /* includeDrawNode */
56298 if (d && d.properties && d.properties.nope || context.surface().classed('nope')) {
56299 if (!_pointerHasMoved) {
56300 // prevent the temporary draw node from appearing on touch devices
56304 dispatch$1.call('rejectedSelfIntersection', this);
56305 return; // can't click here
56308 context.pauseChangeDispatch();
56309 doAdd(); // we just replaced the temporary edit with the real one
56311 _didResolveTempEdit = true;
56312 context.resumeChangeDispatch();
56313 context.enter(mode);
56314 } // Accept the current position of the drawing node
56317 drawWay.add = function (loc, d) {
56318 attemptAdd(d, loc, function () {// don't need to do anything extra
56320 }; // Connect the way to an existing way
56323 drawWay.addWay = function (loc, edge, d) {
56324 attemptAdd(d, loc, function () {
56325 context.replace(actionAddMidpoint({
56328 }, _drawNode), _annotation);
56330 }; // Connect the way to an existing node
56333 drawWay.addNode = function (node, d) {
56334 // finish drawing if the mapper targets the prior node
56335 if (node.id === _headNodeID || // or the first node when drawing an area
56336 _origWay.isClosed() && node.id === _origWay.first()) {
56341 attemptAdd(d, node.loc, function () {
56342 context.replace(function actionReplaceDrawNode(graph) {
56343 // remove the temporary draw node and insert the existing node
56344 // at the same index
56345 graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
56346 return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
56349 }; // Finish the draw operation, removing the temporary edit.
56350 // If the way has enough nodes to be valid, it's selected.
56351 // Otherwise, delete everything and return to browse mode.
56354 drawWay.finish = function () {
56355 checkGeometry(false
56356 /* includeDrawNode */
56359 if (context.surface().classed('nope')) {
56360 dispatch$1.call('rejectedSelfIntersection', this);
56361 return; // can't click here
56364 context.pauseChangeDispatch(); // remove the temporary edit
56367 _didResolveTempEdit = true;
56368 context.resumeChangeDispatch();
56369 var way = context.hasEntity(wayID);
56371 if (!way || way.isDegenerate()) {
56376 window.setTimeout(function () {
56377 context.map().dblclickZoomEnable(true);
56379 var isNewFeature = !mode.isContinuing;
56380 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
56381 }; // Cancel the draw operation, delete everything, and return to browse mode.
56384 drawWay.cancel = function () {
56385 context.pauseChangeDispatch();
56386 resetToStartGraph();
56387 context.resumeChangeDispatch();
56388 window.setTimeout(function () {
56389 context.map().dblclickZoomEnable(true);
56391 context.surface().classed('nope', false).classed('nope-disabled', false).classed('nope-suppressed', false);
56392 context.enter(modeBrowse(context));
56395 drawWay.nodeIndex = function (val) {
56396 if (!arguments.length) return _nodeIndex;
56401 drawWay.activeID = function () {
56402 if (!arguments.length) return _drawNode && _drawNode.id; // no assign
56407 return utilRebind(drawWay, dispatch$1, 'on');
56410 function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
56415 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawLine', function () {
56416 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.lines'))();
56418 mode.wayID = wayID;
56419 mode.isContinuing = continuing;
56421 mode.enter = function () {
56422 behavior.nodeIndex(affix === 'prefix' ? 0 : undefined);
56423 context.install(behavior);
56426 mode.exit = function () {
56427 context.uninstall(behavior);
56430 mode.selectedIDs = function () {
56434 mode.activeID = function () {
56435 return behavior && behavior.activeID() || [];
56441 function operationContinue(context, selectedIDs) {
56442 var _entities = selectedIDs.map(function (id) {
56443 return context.graph().entity(id);
56446 var _geometries = Object.assign({
56449 }, utilArrayGroupBy(_entities, function (entity) {
56450 return entity.geometry(context.graph());
56453 var _vertex = _geometries.vertex.length && _geometries.vertex[0];
56455 function candidateWays() {
56456 return _vertex ? context.graph().parentWays(_vertex).filter(function (parent) {
56457 return parent.geometry(context.graph()) === 'line' && !parent.isClosed() && parent.affix(_vertex.id) && (_geometries.line.length === 0 || _geometries.line[0] === parent);
56461 var _candidates = candidateWays();
56463 var operation = function operation() {
56464 var candidate = _candidates[0];
56465 context.enter(modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(_vertex.id), true));
56468 operation.relatedEntityIds = function () {
56469 return _candidates.length ? [_candidates[0].id] : [];
56472 operation.available = function () {
56473 return _geometries.vertex.length === 1 && _geometries.line.length <= 1 && !context.features().hasHiddenConnections(_vertex, context.graph());
56476 operation.disabled = function () {
56477 if (_candidates.length === 0) {
56478 return 'not_eligible';
56479 } else if (_candidates.length > 1) {
56486 operation.tooltip = function () {
56487 var disable = operation.disabled();
56488 return disable ? _t('operations.continue.' + disable) : _t('operations.continue.description');
56491 operation.annotation = function () {
56492 return _t('operations.continue.annotation.line');
56495 operation.id = 'continue';
56496 operation.keys = [_t('operations.continue.key')];
56497 operation.title = _t('operations.continue.title');
56498 operation.behavior = behaviorOperation(context).which(operation);
56502 function operationCopy(context, selectedIDs) {
56503 function getFilteredIdsToCopy() {
56504 return selectedIDs.filter(function (selectedID) {
56505 var entity = context.graph().hasEntity(selectedID); // don't copy untagged vertices separately from ways
56507 return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
56511 var operation = function operation() {
56512 var graph = context.graph();
56513 var selected = groupEntities(getFilteredIdsToCopy(), graph);
56519 for (i = 0; i < selected.relation.length; i++) {
56520 entity = selected.relation[i];
56522 if (!skip[entity.id] && entity.isComplete(graph)) {
56523 canCopy.push(entity.id);
56524 skip = getDescendants(entity.id, graph, skip);
56528 for (i = 0; i < selected.way.length; i++) {
56529 entity = selected.way[i];
56531 if (!skip[entity.id]) {
56532 canCopy.push(entity.id);
56533 skip = getDescendants(entity.id, graph, skip);
56537 for (i = 0; i < selected.node.length; i++) {
56538 entity = selected.node[i];
56540 if (!skip[entity.id]) {
56541 canCopy.push(entity.id);
56545 context.copyIDs(canCopy);
56547 if (_point && (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
56548 // store the anchor coordinates if copying more than a single node
56549 context.copyLonLat(context.projection.invert(_point));
56551 context.copyLonLat(null);
56555 function groupEntities(ids, graph) {
56556 var entities = ids.map(function (id) {
56557 return graph.entity(id);
56559 return Object.assign({
56563 }, utilArrayGroupBy(entities, 'type'));
56566 function getDescendants(id, graph, descendants) {
56567 var entity = graph.entity(id);
56569 descendants = descendants || {};
56571 if (entity.type === 'relation') {
56572 children = entity.members.map(function (m) {
56575 } else if (entity.type === 'way') {
56576 children = entity.nodes;
56581 for (var i = 0; i < children.length; i++) {
56582 if (!descendants[children[i]]) {
56583 descendants[children[i]] = true;
56584 descendants = getDescendants(children[i], graph, descendants);
56588 return descendants;
56591 operation.available = function () {
56592 return getFilteredIdsToCopy().length > 0;
56595 operation.disabled = function () {
56596 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
56598 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
56599 return 'too_large';
56605 operation.availableForKeypress = function () {
56606 var selection = window.getSelection && window.getSelection(); // if the user has text selected then let them copy that, not the selected feature
56608 return !selection || !selection.toString();
56611 operation.tooltip = function () {
56612 var disable = operation.disabled();
56613 return disable ? _t('operations.copy.' + disable, {
56614 n: selectedIDs.length
56615 }) : _t('operations.copy.description', {
56616 n: selectedIDs.length
56620 operation.annotation = function () {
56621 return _t('operations.copy.annotation', {
56622 n: selectedIDs.length
56628 operation.point = function (val) {
56633 operation.id = 'copy';
56634 operation.keys = [uiCmd('⌘C')];
56635 operation.title = _t('operations.copy.title');
56636 operation.behavior = behaviorOperation(context).which(operation);
56640 function operationDisconnect(context, selectedIDs) {
56641 var _vertexIDs = [];
56643 var _otherIDs = [];
56645 selectedIDs.forEach(function (id) {
56646 var entity = context.entity(id);
56648 if (entity.type === 'way') {
56650 } else if (entity.geometry(context.graph()) === 'vertex') {
56651 _vertexIDs.push(id);
56653 _otherIDs.push(id);
56658 _descriptionID = '',
56659 _annotationID = 'features';
56661 var _disconnectingVertexIds = [];
56662 var _disconnectingWayIds = [];
56664 if (_vertexIDs.length > 0) {
56665 // At the selected vertices, disconnect the selected ways, if any, else
56666 // disconnect all connected ways
56667 _disconnectingVertexIds = _vertexIDs;
56669 _vertexIDs.forEach(function (vertexID) {
56670 var action = actionDisconnect(vertexID);
56672 if (_wayIDs.length > 0) {
56673 var waysIDsForVertex = _wayIDs.filter(function (wayID) {
56674 var way = context.entity(wayID);
56675 return way.nodes.indexOf(vertexID) !== -1;
56678 action.limitWays(waysIDsForVertex);
56681 _actions.push(action);
56683 _disconnectingWayIds = _disconnectingWayIds.concat(context.graph().parentWays(context.graph().entity(vertexID)).map(function (d) {
56688 _disconnectingWayIds = utilArrayUniq(_disconnectingWayIds).filter(function (id) {
56689 return _wayIDs.indexOf(id) === -1;
56691 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
56693 if (_wayIDs.length === 1) {
56694 _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
56696 _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
56698 } else if (_wayIDs.length > 0) {
56699 // Disconnect the selected ways from each other, if they're connected,
56700 // else disconnect them from all connected ways
56701 var ways = _wayIDs.map(function (id) {
56702 return context.entity(id);
56705 var nodes = utilGetAllNodes(_wayIDs, context.graph());
56706 _coords = nodes.map(function (n) {
56708 }); // actions for connected nodes shared by at least two selected ways
56710 var sharedActions = [];
56711 var sharedNodes = []; // actions for connected nodes
56713 var unsharedActions = [];
56714 var unsharedNodes = [];
56715 nodes.forEach(function (node) {
56716 var action = actionDisconnect(node.id).limitWays(_wayIDs);
56718 if (action.disabled(context.graph()) !== 'not_connected') {
56721 for (var i in ways) {
56724 if (way.nodes.indexOf(node.id) !== -1) {
56728 if (count > 1) break;
56732 sharedActions.push(action);
56733 sharedNodes.push(node);
56735 unsharedActions.push(action);
56736 unsharedNodes.push(node);
56740 _descriptionID += 'no_points.';
56741 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
56743 if (sharedActions.length) {
56744 // if any nodes are shared, only disconnect the selected ways from each other
56745 _actions = sharedActions;
56746 _disconnectingVertexIds = sharedNodes.map(function (node) {
56749 _descriptionID += 'conjoined';
56750 _annotationID = 'from_each_other';
56752 // if no nodes are shared, disconnect the selected ways from all connected ways
56753 _actions = unsharedActions;
56754 _disconnectingVertexIds = unsharedNodes.map(function (node) {
56758 if (_wayIDs.length === 1) {
56759 _descriptionID += context.graph().geometry(_wayIDs[0]);
56761 _descriptionID += 'separate';
56766 var _extent = utilTotalExtent(_disconnectingVertexIds, context.graph());
56768 var operation = function operation() {
56769 context.perform(function (graph) {
56770 return _actions.reduce(function (graph, action) {
56771 return action(graph);
56773 }, operation.annotation());
56774 context.validator().validate();
56777 operation.relatedEntityIds = function () {
56778 if (_vertexIDs.length) {
56779 return _disconnectingWayIds;
56782 return _disconnectingVertexIds;
56785 operation.available = function () {
56786 if (_actions.length === 0) return false;
56787 if (_otherIDs.length !== 0) return false;
56788 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function (wayID) {
56789 return _vertexIDs.some(function (vertexID) {
56790 var way = context.entity(wayID);
56791 return way.nodes.indexOf(vertexID) !== -1;
56797 operation.disabled = function () {
56800 for (var actionIndex in _actions) {
56801 reason = _actions[actionIndex].disabled(context.graph());
56802 if (reason) return reason;
56805 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
56806 return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
56807 } else if (_coords && someMissing()) {
56808 return 'not_downloaded';
56809 } else if (selectedIDs.some(context.hasHiddenConnections)) {
56810 return 'connected_to_hidden';
56815 function someMissing() {
56816 if (context.inIntro()) return false;
56817 var osm = context.connection();
56820 var missing = _coords.filter(function (loc) {
56821 return !osm.isDataLoaded(loc);
56824 if (missing.length) {
56825 missing.forEach(function (loc) {
56826 context.loadTileAtLoc(loc);
56836 operation.tooltip = function () {
56837 var disable = operation.disabled();
56840 return _t('operations.disconnect.' + disable);
56843 return _t('operations.disconnect.description.' + _descriptionID);
56846 operation.annotation = function () {
56847 return _t('operations.disconnect.annotation.' + _annotationID);
56850 operation.id = 'disconnect';
56851 operation.keys = [_t('operations.disconnect.key')];
56852 operation.title = _t('operations.disconnect.title');
56853 operation.behavior = behaviorOperation(context).which(operation);
56857 function operationDowngrade(context, selectedIDs) {
56858 var _affectedFeatureCount = 0;
56860 var _downgradeType = downgradeTypeForEntityIDs(selectedIDs);
56862 var _multi = _affectedFeatureCount === 1 ? 'single' : 'multiple';
56864 function downgradeTypeForEntityIDs(entityIds) {
56866 _affectedFeatureCount = 0;
56868 for (var i in entityIds) {
56869 var entityID = entityIds[i];
56870 var type = downgradeTypeForEntityID(entityID);
56873 _affectedFeatureCount += 1;
56875 if (downgradeType && type !== downgradeType) {
56876 if (downgradeType !== 'generic' && type !== 'generic') {
56877 downgradeType = 'building_address';
56879 downgradeType = 'generic';
56882 downgradeType = type;
56887 return downgradeType;
56890 function downgradeTypeForEntityID(entityID) {
56891 var graph = context.graph();
56892 var entity = graph.entity(entityID);
56893 var preset = _mainPresetIndex.match(entity, graph);
56894 if (!preset || preset.isFallback()) return null;
56896 if (entity.type === 'node' && preset.id !== 'address' && Object.keys(entity.tags).some(function (key) {
56897 return key.match(/^addr:.{1,}/);
56902 var geometry = entity.geometry(graph);
56904 if (geometry === 'area' && entity.tags.building && !preset.tags.building) {
56908 if (geometry === 'vertex' && Object.keys(entity.tags).length) {
56915 var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
56916 var addressKeysToKeep = ['source'];
56918 var operation = function operation() {
56919 context.perform(function (graph) {
56920 for (var i in selectedIDs) {
56921 var entityID = selectedIDs[i];
56922 var type = downgradeTypeForEntityID(entityID);
56923 if (!type) continue;
56924 var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy
56926 for (var key in tags) {
56927 if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
56929 if (type === 'building') {
56930 if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
56933 if (type !== 'generic' && key.match(/^addr:.{1,}/)) continue;
56937 graph = actionChangeTags(entityID, tags)(graph);
56941 }, operation.annotation());
56942 context.validator().validate(); // refresh the select mode to enable the delete operation
56944 context.enter(modeSelect(context, selectedIDs));
56947 operation.available = function () {
56948 return _downgradeType;
56951 operation.disabled = function () {
56952 if (selectedIDs.some(hasWikidataTag)) {
56953 return 'has_wikidata_tag';
56958 function hasWikidataTag(id) {
56959 var entity = context.entity(id);
56960 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
56964 operation.tooltip = function () {
56965 var disable = operation.disabled();
56966 return disable ? _t('operations.downgrade.' + disable + '.' + _multi) : _t('operations.downgrade.description.' + _downgradeType);
56969 operation.annotation = function () {
56972 if (_downgradeType === 'building_address') {
56973 suffix = 'generic';
56975 suffix = _downgradeType;
56978 return _t('operations.downgrade.annotation.' + suffix, {
56979 n: _affectedFeatureCount
56983 operation.id = 'downgrade';
56984 operation.keys = [uiCmd('⌫')];
56985 operation.title = _t('operations.downgrade.title');
56986 operation.behavior = behaviorOperation(context).which(operation);
56990 function operationExtract(context, selectedIDs) {
56991 var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
56993 var _geometries = utilArrayUniq(selectedIDs.map(function (entityID) {
56994 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
56995 }).filter(Boolean));
56997 var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
57001 var _actions = selectedIDs.map(function (entityID) {
57002 var graph = context.graph();
57003 var entity = graph.hasEntity(entityID);
57004 if (!entity || !entity.hasInterestingTags()) return null;
57005 if (entity.type === 'node' && graph.parentWays(entity).length === 0) return null;
57007 if (entity.type !== 'node') {
57008 var preset = _mainPresetIndex.match(entity, graph); // only allow extraction from ways/relations if the preset supports points
57010 if (preset.geometry.indexOf('point') === -1) return null;
57013 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
57014 return actionExtract(entityID);
57015 }).filter(Boolean);
57017 var operation = function operation() {
57018 var combinedAction = function combinedAction(graph) {
57019 _actions.forEach(function (action) {
57020 graph = action(graph);
57026 context.perform(combinedAction, operation.annotation()); // do the extract
57028 var extractedNodeIDs = _actions.map(function (action) {
57029 return action.getExtractedNodeID();
57032 context.enter(modeSelect(context, extractedNodeIDs));
57035 operation.available = function () {
57036 return _actions.length && selectedIDs.length === _actions.length;
57039 operation.disabled = function () {
57040 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
57041 return 'too_large';
57042 } else if (selectedIDs.some(function (entityID) {
57043 return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
57045 return 'connected_to_hidden';
57051 operation.tooltip = function () {
57052 var disableReason = operation.disabled();
57054 if (disableReason) {
57055 return _t('operations.extract.' + disableReason + '.' + _amount);
57057 return _t('operations.extract.description.' + _geometryID + '.' + _amount);
57061 operation.annotation = function () {
57062 return _t('operations.extract.annotation', {
57063 n: selectedIDs.length
57067 operation.id = 'extract';
57068 operation.keys = [_t('operations.extract.key')];
57069 operation.title = _t('operations.extract.title');
57070 operation.behavior = behaviorOperation(context).which(operation);
57074 function operationMerge(context, selectedIDs) {
57075 var _action = getAction();
57077 function getAction() {
57078 // prefer a non-disabled action first
57079 var join = actionJoin(selectedIDs);
57080 if (!join.disabled(context.graph())) return join;
57081 var merge = actionMerge(selectedIDs);
57082 if (!merge.disabled(context.graph())) return merge;
57083 var mergePolygon = actionMergePolygon(selectedIDs);
57084 if (!mergePolygon.disabled(context.graph())) return mergePolygon;
57085 var mergeNodes = actionMergeNodes(selectedIDs);
57086 if (!mergeNodes.disabled(context.graph())) return mergeNodes; // otherwise prefer an action with an interesting disabled reason
57088 if (join.disabled(context.graph()) !== 'not_eligible') return join;
57089 if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
57090 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
57094 var operation = function operation() {
57095 if (operation.disabled()) return;
57096 context.perform(_action, operation.annotation());
57097 context.validator().validate();
57098 var resultIDs = selectedIDs.filter(context.hasEntity);
57100 if (resultIDs.length > 1) {
57101 var interestingIDs = resultIDs.filter(function (id) {
57102 return context.entity(id).hasInterestingTags();
57104 if (interestingIDs.length) resultIDs = interestingIDs;
57107 context.enter(modeSelect(context, resultIDs));
57110 operation.available = function () {
57111 return selectedIDs.length >= 2;
57114 operation.disabled = function () {
57115 var actionDisabled = _action.disabled(context.graph());
57117 if (actionDisabled) return actionDisabled;
57118 var osm = context.connection();
57120 if (osm && _action.resultingWayNodesLength && _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
57121 return 'too_many_vertices';
57127 operation.tooltip = function () {
57128 var disabled = operation.disabled();
57131 if (disabled === 'restriction') {
57132 return _t('operations.merge.restriction', {
57133 relation: _mainPresetIndex.item('type/restriction').name()
57137 return _t('operations.merge.' + disabled);
57140 return _t('operations.merge.description');
57143 operation.annotation = function () {
57144 return _t('operations.merge.annotation', {
57145 n: selectedIDs.length
57149 operation.id = 'merge';
57150 operation.keys = [_t('operations.merge.key')];
57151 operation.title = _t('operations.merge.title');
57152 operation.behavior = behaviorOperation(context).which(operation);
57156 function operationPaste(context) {
57159 var operation = function operation() {
57160 if (!_pastePoint) return;
57161 var oldIDs = context.copyIDs();
57162 if (!oldIDs.length) return;
57163 var projection = context.projection;
57164 var extent = geoExtent();
57165 var oldGraph = context.copyGraph();
57167 var action = actionCopyEntities(oldIDs, oldGraph);
57168 context.perform(action);
57169 var copies = action.copies();
57170 var originals = new Set();
57171 Object.values(copies).forEach(function (entity) {
57172 originals.add(entity.id);
57175 for (var id in copies) {
57176 var oldEntity = oldGraph.entity(id);
57177 var newEntity = copies[id];
57179 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
57182 var parents = context.graph().parentWays(newEntity);
57183 var parentCopied = parents.some(function (parent) {
57184 return originals.has(parent.id);
57187 if (!parentCopied) {
57188 newIDs.push(newEntity.id);
57190 } // Use the location of the copy operation to offset the paste location,
57191 // or else use the center of the pasted extent
57194 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
57195 var delta = geoVecSubtract(_pastePoint, copyPoint); // Move the pasted objects to be anchored at the paste location
57197 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
57198 context.enter(modeSelect(context, newIDs));
57201 operation.point = function (val) {
57206 operation.available = function () {
57207 return context.mode().id === 'browse';
57210 operation.disabled = function () {
57211 return !context.copyIDs().length;
57214 operation.tooltip = function () {
57215 var oldGraph = context.copyGraph();
57216 var ids = context.copyIDs();
57219 return _t('operations.paste.nothing_copied');
57222 return _t('operations.paste.description', {
57223 feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph),
57228 operation.annotation = function () {
57229 var ids = context.copyIDs();
57230 return _t('operations.paste.annotation', {
57235 operation.id = 'paste';
57236 operation.keys = [uiCmd('⌘V')];
57237 operation.title = _t('operations.paste.title');
57241 function operationReverse(context, selectedIDs) {
57242 var operation = function operation() {
57243 context.perform(function combinedReverseAction(graph) {
57244 actions().forEach(function (action) {
57245 graph = action(graph);
57248 }, operation.annotation());
57249 context.validator().validate();
57252 function actions(situation) {
57253 return selectedIDs.map(function (entityID) {
57254 var entity = context.hasEntity(entityID);
57255 if (!entity) return null;
57257 if (situation === 'toolbar') {
57258 if (entity.type === 'way' && !entity.isOneWay() && !entity.isSided()) return null;
57261 var geometry = entity.geometry(context.graph());
57262 if (entity.type !== 'node' && geometry !== 'line') return null;
57263 var action = actionReverse(entityID);
57264 if (action.disabled(context.graph())) return null;
57266 }).filter(Boolean);
57269 function reverseTypeID() {
57270 var acts = actions();
57271 var nodeActionCount = acts.filter(function (act) {
57272 var entity = context.hasEntity(act.entityID());
57273 return entity && entity.type === 'node';
57275 if (nodeActionCount === 0) return 'line';
57276 if (nodeActionCount === acts.length) return 'point';
57280 operation.available = function (situation) {
57281 return actions(situation).length > 0;
57284 operation.disabled = function () {
57288 operation.tooltip = function () {
57289 return _t('operations.reverse.description.' + reverseTypeID());
57292 operation.annotation = function () {
57293 var acts = actions();
57294 return _t('operations.reverse.annotation.' + reverseTypeID(), {
57299 operation.id = 'reverse';
57300 operation.keys = [_t('operations.reverse.key')];
57301 operation.title = _t('operations.reverse.title');
57302 operation.behavior = behaviorOperation(context).which(operation);
57306 function operationSplit(context, selectedIDs) {
57307 var _vertexIds = selectedIDs.filter(function (id) {
57308 return context.graph().geometry(id) === 'vertex';
57311 var _selectedWayIds = selectedIDs.filter(function (id) {
57312 var entity = context.graph().hasEntity(id);
57313 return entity && entity.type === 'way';
57316 var _isAvailable = _vertexIds.length > 0 && _vertexIds.length + _selectedWayIds.length === selectedIDs.length;
57318 var _action = actionSplit(_vertexIds);
57321 var _geometry = 'feature';
57322 var _waysAmount = 'single';
57324 var _nodesAmount = _vertexIds.length === 1 ? 'single' : 'multiple';
57326 if (_isAvailable) {
57327 if (_selectedWayIds.length) _action.limitWays(_selectedWayIds);
57328 _ways = _action.ways(context.graph());
57329 var geometries = {};
57331 _ways.forEach(function (way) {
57332 geometries[way.geometry(context.graph())] = true;
57335 if (Object.keys(geometries).length === 1) {
57336 _geometry = Object.keys(geometries)[0];
57339 _waysAmount = _ways.length === 1 ? 'single' : 'multiple';
57342 var operation = function operation() {
57343 var difference = context.perform(_action, operation.annotation()); // select both the nodes and the ways so the mapper can immediately disconnect them if desired
57345 var idsToSelect = _vertexIds.concat(difference.extantIDs().filter(function (id) {
57346 // filter out relations that may have had member additions
57347 return context.entity(id).type === 'way';
57350 context.enter(modeSelect(context, idsToSelect));
57353 operation.relatedEntityIds = function () {
57354 return _selectedWayIds.length ? [] : _ways.map(function (way) {
57359 operation.available = function () {
57360 return _isAvailable;
57363 operation.disabled = function () {
57364 var reason = _action.disabled(context.graph());
57368 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57369 return 'connected_to_hidden';
57375 operation.tooltip = function () {
57376 var disable = operation.disabled();
57377 if (disable) return _t('operations.split.' + disable);
57378 return _t('operations.split.description.' + _geometry + '.' + _waysAmount + '.' + _nodesAmount + '_node');
57381 operation.annotation = function () {
57382 return _t('operations.split.annotation.' + _geometry, {
57387 operation.id = 'split';
57388 operation.keys = [_t('operations.split.key')];
57389 operation.title = _t('operations.split.title');
57390 operation.behavior = behaviorOperation(context).which(operation);
57394 function operationStraighten(context, selectedIDs) {
57395 var _wayIDs = selectedIDs.filter(function (id) {
57396 return id.charAt(0) === 'w';
57399 var _nodeIDs = selectedIDs.filter(function (id) {
57400 return id.charAt(0) === 'n';
57403 var _amount = (_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple';
57405 var _nodes = utilGetAllNodes(selectedIDs, context.graph());
57407 var _coords = _nodes.map(function (n) {
57411 var _extent = utilTotalExtent(selectedIDs, context.graph());
57413 var _action = chooseAction();
57417 function chooseAction() {
57418 // straighten selected nodes
57419 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
57420 _geometry = 'point';
57421 return actionStraightenNodes(_nodeIDs, context.projection); // straighten selected ways (possibly between range of 2 selected nodes)
57422 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
57423 var startNodeIDs = [];
57424 var endNodeIDs = [];
57426 for (var i = 0; i < selectedIDs.length; i++) {
57427 var entity = context.entity(selectedIDs[i]);
57429 if (entity.type === 'node') {
57431 } else if (entity.type !== 'way' || entity.isClosed()) {
57432 return null; // exit early, can't straighten these
57435 startNodeIDs.push(entity.first());
57436 endNodeIDs.push(entity.last());
57437 } // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
57440 startNodeIDs = startNodeIDs.filter(function (n) {
57441 return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
57443 endNodeIDs = endNodeIDs.filter(function (n) {
57444 return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
57445 }); // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
57447 if (utilArrayDifference(startNodeIDs, endNodeIDs).length + utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null; // Ensure path contains at least 3 unique nodes
57449 var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph()).map(function (node) {
57452 if (wayNodeIDs.length <= 2) return null; // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
57454 if (_nodeIDs.length === 2 && (wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1)) return null;
57456 if (_nodeIDs.length) {
57457 // If we're only straightenting between two points, we only need that extent visible
57458 _extent = utilTotalExtent(_nodeIDs, context.graph());
57461 _geometry = 'line';
57462 return actionStraightenWay(selectedIDs, context.projection);
57468 function operation() {
57469 if (!_action) return;
57470 context.perform(_action, operation.annotation());
57471 window.setTimeout(function () {
57472 context.validator().validate();
57473 }, 300); // after any transition
57476 operation.available = function () {
57477 return Boolean(_action);
57480 operation.disabled = function () {
57481 var reason = _action.disabled(context.graph());
57485 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
57486 return 'too_large';
57487 } else if (someMissing()) {
57488 return 'not_downloaded';
57489 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57490 return 'connected_to_hidden';
57495 function someMissing() {
57496 if (context.inIntro()) return false;
57497 var osm = context.connection();
57500 var missing = _coords.filter(function (loc) {
57501 return !osm.isDataLoaded(loc);
57504 if (missing.length) {
57505 missing.forEach(function (loc) {
57506 context.loadTileAtLoc(loc);
57516 operation.tooltip = function () {
57517 var disable = operation.disabled();
57518 return disable ? _t('operations.straighten.' + disable + '.' + _amount) : _t('operations.straighten.description.' + _geometry + (_wayIDs.length === 1 ? '' : 's'));
57521 operation.annotation = function () {
57522 return _t('operations.straighten.annotation.' + _geometry, {
57523 n: _wayIDs.length ? _wayIDs.length : _nodeIDs.length
57527 operation.id = 'straighten';
57528 operation.keys = [_t('operations.straighten.key')];
57529 operation.title = _t('operations.straighten.title');
57530 operation.behavior = behaviorOperation(context).which(operation);
57534 var Operations = /*#__PURE__*/Object.freeze({
57536 operationCircularize: operationCircularize,
57537 operationContinue: operationContinue,
57538 operationCopy: operationCopy,
57539 operationDelete: operationDelete,
57540 operationDisconnect: operationDisconnect,
57541 operationDowngrade: operationDowngrade,
57542 operationExtract: operationExtract,
57543 operationMerge: operationMerge,
57544 operationMove: operationMove,
57545 operationOrthogonalize: operationOrthogonalize,
57546 operationPaste: operationPaste,
57547 operationReflectShort: operationReflectShort,
57548 operationReflectLong: operationReflectLong,
57549 operationReverse: operationReverse,
57550 operationRotate: operationRotate,
57551 operationSplit: operationSplit,
57552 operationStraighten: operationStraighten
57555 var _relatedParent;
57557 function modeSelect(context, selectedIDs) {
57562 var keybinding = utilKeybinding('select');
57564 var _breatheBehavior = behaviorBreathe();
57566 var _modeDragNode = modeDragNode(context);
57568 var _selectBehavior;
57570 var _behaviors = [];
57571 var _operations = [];
57572 var _newFeature = false;
57573 var _follow = false;
57575 function singular() {
57576 if (selectedIDs && selectedIDs.length === 1) {
57577 return context.hasEntity(selectedIDs[0]);
57581 function selectedEntities() {
57582 return selectedIDs.map(function (id) {
57583 return context.hasEntity(id);
57584 }).filter(Boolean);
57587 function checkSelectedIDs() {
57590 if (Array.isArray(selectedIDs)) {
57591 ids = selectedIDs.filter(function (id) {
57592 return context.hasEntity(id);
57597 context.enter(modeBrowse(context));
57599 } else if (selectedIDs.length > 1 && ids.length === 1 || selectedIDs.length === 1 && ids.length > 1) {
57600 // switch between single- and multi-select UI
57601 context.enter(modeSelect(context, ids));
57607 } // find the common parent ways for nextVertex, previousVertex
57610 function commonParents() {
57611 var graph = context.graph();
57612 var commonParents = [];
57614 for (var i = 0; i < selectedIDs.length; i++) {
57615 var entity = context.hasEntity(selectedIDs[i]);
57617 if (!entity || entity.geometry(graph) !== 'vertex') {
57618 return []; // selection includes some not vertices
57621 var currParents = graph.parentWays(entity).map(function (w) {
57625 if (!commonParents.length) {
57626 commonParents = currParents;
57630 commonParents = utilArrayIntersection(commonParents, currParents);
57632 if (!commonParents.length) {
57637 return commonParents;
57640 function singularParent() {
57641 var parents = commonParents();
57643 if (!parents || parents.length === 0) {
57644 _relatedParent = null;
57646 } // relatedParent is used when we visit a vertex with multiple
57647 // parents, and we want to remember which parent line we started on.
57650 if (parents.length === 1) {
57651 _relatedParent = parents[0]; // remember this parent for later
57653 return _relatedParent;
57656 if (parents.indexOf(_relatedParent) !== -1) {
57657 return _relatedParent; // prefer the previously seen parent
57663 mode.selectedIDs = function (val) {
57664 if (!arguments.length) return selectedIDs;
57669 mode.zoomToSelected = function () {
57670 context.map().zoomToEase(selectedEntities());
57673 mode.newFeature = function (val) {
57674 if (!arguments.length) return _newFeature;
57679 mode.selectBehavior = function (val) {
57680 if (!arguments.length) return _selectBehavior;
57681 _selectBehavior = val;
57685 mode.follow = function (val) {
57686 if (!arguments.length) return _follow;
57691 function loadOperations() {
57692 _operations.forEach(function (operation) {
57693 if (operation.behavior) {
57694 context.uninstall(operation.behavior);
57698 _operations = Object.values(Operations).map(function (o) {
57699 return o(context, selectedIDs);
57700 }).filter(function (o) {
57701 return o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy';
57702 }).concat([// group copy/downgrade/delete operation together at the end of the list
57703 operationCopy(context, selectedIDs), operationDowngrade(context, selectedIDs), operationDelete(context, selectedIDs)]).filter(function (operation) {
57704 return operation.available();
57707 _operations.forEach(function (operation) {
57708 if (operation.behavior) {
57709 context.install(operation.behavior);
57711 }); // remove any displayed menu
57714 context.ui().closeEditMenu();
57717 mode.operations = function () {
57718 return _operations;
57721 mode.enter = function () {
57722 if (!checkSelectedIDs()) return;
57723 context.features().forceVisible(selectedIDs);
57725 _modeDragNode.restoreSelectedIDs(selectedIDs);
57729 if (!_behaviors.length) {
57730 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
57731 _behaviors = [behaviorPaste(context), _breatheBehavior, behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect), _selectBehavior, behaviorLasso(context), _modeDragNode.behavior, modeDragNote(context).behavior];
57734 _behaviors.forEach(context.install);
57736 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) {
57737 return uiCmd('⇧' + key);
57738 }), scaleSelection(1.05)).on(utilKeybinding.plusKeys.map(function (key) {
57739 return uiCmd('⇧⌥' + key);
57740 }), scaleSelection(Math.pow(1.05, 5))).on(utilKeybinding.minusKeys.map(function (key) {
57741 return uiCmd('⇧' + key);
57742 }), scaleSelection(1 / 1.05)).on(utilKeybinding.minusKeys.map(function (key) {
57743 return uiCmd('⇧⌥' + key);
57744 }), scaleSelection(1 / Math.pow(1.05, 5))).on(['\\', 'pause'], nextParent).on('⎋', esc, true);
57745 select(document).call(keybinding);
57746 context.ui().sidebar.select(selectedIDs, _newFeature);
57747 context.history().on('change.select', function () {
57748 loadOperations(); // reselect after change in case relation members were removed or added
57751 }).on('undone.select', checkSelectedIDs).on('redone.select', checkSelectedIDs);
57752 context.map().on('drawn.select', selectElements).on('crossEditableZoom.select', function () {
57755 _breatheBehavior.restartIfNeeded(context.surface());
57757 context.map().doubleUpHandler().on('doubleUp.modeSelect', didDoubleUp);
57761 var extent = geoExtent();
57762 var graph = context.graph();
57763 selectedIDs.forEach(function (id) {
57764 var entity = context.entity(id);
57766 extent._extend(entity.extent(graph));
57768 var loc = extent.center();
57769 context.map().centerEase(loc); // we could enter the mode multiple times, so reset follow for next time
57774 function nudgeSelection(delta) {
57775 return function () {
57776 // prevent nudging during low zoom selection
57777 if (!context.map().withinEditableZoom()) return;
57778 var moveOp = operationMove(context, selectedIDs);
57780 if (moveOp.disabled()) {
57781 context.ui().flash.duration(4000).iconName('#iD-operation-' + moveOp.id).iconClass('operation disabled').label(moveOp.tooltip)();
57783 context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
57784 context.validator().validate();
57789 function scaleSelection(factor) {
57790 return function () {
57791 // prevent scaling during low zoom selection
57792 if (!context.map().withinEditableZoom()) return;
57793 var nodes = utilGetAllNodes(selectedIDs, context.graph());
57794 var isUp = factor > 1; // can only scale if multiple nodes are selected
57796 if (nodes.length <= 1) return;
57797 var extent = utilTotalExtent(selectedIDs, context.graph()); // These disabled checks would normally be handled by an operation
57798 // object, but we don't want an actual scale operation at this point.
57800 function scalingDisabled() {
57802 return 'too_small';
57803 } else if (extent.percentContainedIn(context.map().extent()) < 0.8) {
57804 return 'too_large';
57805 } else if (someMissing() || selectedIDs.some(incompleteRelation)) {
57806 return 'not_downloaded';
57807 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57808 return 'connected_to_hidden';
57813 function tooSmall() {
57814 if (isUp) return false;
57815 var dLon = Math.abs(extent[1][0] - extent[0][0]);
57816 var dLat = Math.abs(extent[1][1] - extent[0][1]);
57817 return dLon < geoMetersToLon(1, extent[1][1]) && dLat < geoMetersToLat(1);
57820 function someMissing() {
57821 if (context.inIntro()) return false;
57822 var osm = context.connection();
57825 var missing = nodes.filter(function (n) {
57826 return !osm.isDataLoaded(n.loc);
57829 if (missing.length) {
57830 missing.forEach(function (loc) {
57831 context.loadTileAtLoc(loc);
57840 function incompleteRelation(id) {
57841 var entity = context.entity(id);
57842 return entity.type === 'relation' && !entity.isComplete(context.graph());
57846 var disabled = scalingDisabled();
57849 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
57850 context.ui().flash.duration(4000).iconName('#iD-icon-no').iconClass('operation disabled').label(_t('operations.scale.' + disabled + '.' + multi))();
57852 var pivot = context.projection(extent.center());
57853 var annotation = _t('operations.scale.annotation.' + (isUp ? 'up' : 'down') + '.feature', {
57854 n: selectedIDs.length
57856 context.perform(actionScale(selectedIDs, pivot, factor, context.projection), annotation);
57857 context.validator().validate();
57862 function didDoubleUp(d3_event, loc) {
57863 if (!context.map().withinEditableZoom()) return;
57864 var target = select(d3_event.target);
57865 var datum = target.datum();
57866 var entity = datum && datum.properties && datum.properties.entity;
57867 if (!entity) return;
57869 if (entity instanceof osmWay && target.classed('target')) {
57870 var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
57871 var prev = entity.nodes[choice.index - 1];
57872 var next = entity.nodes[choice.index];
57873 context.perform(actionAddMidpoint({
57876 }, osmNode()), _t('operations.add.annotation.vertex'));
57877 } else if (entity.type === 'midpoint') {
57878 context.perform(actionAddMidpoint({
57881 }, osmNode()), _t('operations.add.annotation.vertex'));
57885 function selectElements() {
57886 if (!checkSelectedIDs()) return;
57887 var surface = context.surface();
57888 surface.selectAll('.selected-member').classed('selected-member', false);
57889 surface.selectAll('.selected').classed('selected', false);
57890 surface.selectAll('.related').classed('related', false);
57893 if (_relatedParent) {
57894 surface.selectAll(utilEntitySelector([_relatedParent])).classed('related', true);
57897 if (context.map().withinEditableZoom()) {
57898 // Apply selection styling if not in wide selection
57899 surface.selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true
57900 /* skipMultipolgonMembers */
57901 )).classed('selected-member', true);
57902 surface.selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())).classed('selected', true);
57907 if (context.container().select('.combobox').size()) return;
57908 context.enter(modeBrowse(context));
57911 function firstVertex(d3_event) {
57912 d3_event.preventDefault();
57913 var entity = singular();
57914 var parent = singularParent();
57917 if (entity && entity.type === 'way') {
57919 } else if (parent) {
57920 way = context.entity(parent);
57924 context.enter(modeSelect(context, [way.first()]).follow(true));
57928 function lastVertex(d3_event) {
57929 d3_event.preventDefault();
57930 var entity = singular();
57931 var parent = singularParent();
57934 if (entity && entity.type === 'way') {
57936 } else if (parent) {
57937 way = context.entity(parent);
57941 context.enter(modeSelect(context, [way.last()]).follow(true));
57945 function previousVertex(d3_event) {
57946 d3_event.preventDefault();
57947 var parent = singularParent();
57948 if (!parent) return;
57949 var way = context.entity(parent);
57950 var length = way.nodes.length;
57951 var curr = way.nodes.indexOf(selectedIDs[0]);
57956 } else if (way.isClosed()) {
57957 index = length - 2;
57960 if (index !== -1) {
57961 context.enter(modeSelect(context, [way.nodes[index]]).follow(true));
57965 function nextVertex(d3_event) {
57966 d3_event.preventDefault();
57967 var parent = singularParent();
57968 if (!parent) return;
57969 var way = context.entity(parent);
57970 var length = way.nodes.length;
57971 var curr = way.nodes.indexOf(selectedIDs[0]);
57974 if (curr < length - 1) {
57976 } else if (way.isClosed()) {
57980 if (index !== -1) {
57981 context.enter(modeSelect(context, [way.nodes[index]]).follow(true));
57985 function nextParent(d3_event) {
57986 d3_event.preventDefault();
57987 var parents = commonParents();
57988 if (!parents || parents.length < 2) return;
57989 var index = parents.indexOf(_relatedParent);
57991 if (index < 0 || index > parents.length - 2) {
57992 _relatedParent = parents[0];
57994 _relatedParent = parents[index + 1];
57997 var surface = context.surface();
57998 surface.selectAll('.related').classed('related', false);
58000 if (_relatedParent) {
58001 surface.selectAll(utilEntitySelector([_relatedParent])).classed('related', true);
58006 mode.exit = function () {
58007 _newFeature = false;
58009 _operations.forEach(function (operation) {
58010 if (operation.behavior) {
58011 context.uninstall(operation.behavior);
58017 _behaviors.forEach(context.uninstall);
58019 select(document).call(keybinding.unbind);
58020 context.ui().closeEditMenu();
58021 context.history().on('change.select', null).on('undone.select', null).on('redone.select', null);
58022 var surface = context.surface();
58023 surface.selectAll('.selected-member').classed('selected-member', false);
58024 surface.selectAll('.selected').classed('selected', false);
58025 surface.selectAll('.highlighted').classed('highlighted', false);
58026 surface.selectAll('.related').classed('related', false);
58027 context.map().on('drawn.select', null);
58028 context.ui().sidebar.hide();
58029 context.features().forceVisible([]);
58030 var entity = singular();
58032 if (_newFeature && entity && entity.type === 'relation' && // no tags
58033 Object.keys(entity.tags).length === 0 && // no parent relations
58034 context.graph().parentRelations(entity).length === 0 && ( // no members or one member with no role
58035 entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
58036 // the user added this relation but didn't edit it at all, so just delete it
58037 var deleteAction = actionDeleteRelation(entity.id, true
58038 /* don't delete untagged members */
58040 context.perform(deleteAction, _t('operations.delete.annotation.relation'));
58047 function uiLasso(context) {
58048 var group, polygon;
58049 lasso.coordinates = [];
58051 function lasso(selection) {
58052 context.container().classed('lasso', true);
58053 group = selection.append('g').attr('class', 'lasso hide');
58054 polygon = group.append('path').attr('class', 'lasso-path');
58055 group.call(uiToggle(true));
58060 polygon.data([lasso.coordinates]).attr('d', function (d) {
58061 return 'M' + d.join(' L') + ' Z';
58066 lasso.extent = function () {
58067 return lasso.coordinates.reduce(function (extent, point) {
58068 return extent.extend(geoExtent(point));
58072 lasso.p = function (_) {
58073 if (!arguments.length) return lasso;
58074 lasso.coordinates.push(_);
58079 lasso.close = function () {
58081 group.call(uiToggle(false, function () {
58082 select(this).remove();
58086 context.container().classed('lasso', false);
58092 function behaviorLasso(context) {
58093 // use pointer events on supported platforms; fallback to mouse events
58094 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
58096 var behavior = function behavior(selection) {
58099 function pointerdown(d3_event) {
58100 var button = 0; // left
58102 if (d3_event.button === button && d3_event.shiftKey === true) {
58104 select(window).on(_pointerPrefix + 'move.lasso', pointermove).on(_pointerPrefix + 'up.lasso', pointerup);
58105 d3_event.stopPropagation();
58109 function pointermove() {
58111 lasso = uiLasso(context);
58112 context.surface().call(lasso);
58115 lasso.p(context.map().mouse());
58118 function normalize(a, b) {
58119 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])]];
58122 function lassoed() {
58123 if (!lasso) return [];
58124 var graph = context.graph();
58127 if (context.map().editableDataEnabled(true
58128 /* skipZoomCheck */
58129 ) && context.map().isInWideSelection()) {
58130 // only select from the visible nodes
58131 limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
58132 } else if (!context.map().editableDataEnabled()) {
58136 var bounds = lasso.extent().map(context.projection.invert);
58137 var extent = geoExtent(normalize(bounds[0], bounds[1]));
58138 var intersects = context.history().intersects(extent).filter(function (entity) {
58139 return entity.type === 'node' && (!limitToNodes || limitToNodes.has(entity)) && geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && !context.features().isHidden(entity, graph, entity.geometry(graph));
58140 }); // sort the lassoed nodes as best we can
58142 intersects.sort(function (node1, node2) {
58143 var parents1 = graph.parentWays(node1);
58144 var parents2 = graph.parentWays(node2);
58146 if (parents1.length && parents2.length) {
58147 // both nodes are vertices
58148 var sharedParents = utilArrayIntersection(parents1, parents2);
58150 if (sharedParents.length) {
58151 var sharedParentNodes = sharedParents[0].nodes; // vertices are members of the same way; sort them in their listed order
58153 return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id);
58155 // vertices do not share a way; group them by their respective parent ways
58156 return parseFloat(parents1[0].id.slice(1)) - parseFloat(parents2[0].id.slice(1));
58158 } else if (parents1.length || parents2.length) {
58159 // only one node is a vertex; sort standalone points before vertices
58160 return parents1.length - parents2.length;
58161 } // both nodes are standalone points; sort left to right
58164 return node1.loc[0] - node2.loc[0];
58166 return intersects.map(function (entity) {
58171 function pointerup() {
58172 select(window).on(_pointerPrefix + 'move.lasso', null).on(_pointerPrefix + 'up.lasso', null);
58173 if (!lasso) return;
58174 var ids = lassoed();
58178 context.enter(modeSelect(context, ids));
58182 selection.on(_pointerPrefix + 'down.lasso', pointerdown);
58185 behavior.off = function (selection) {
58186 selection.on(_pointerPrefix + 'down.lasso', null);
58192 function modeBrowse(context) {
58196 title: _t('modes.browse.title'),
58197 description: _t('modes.browse.description')
58201 var _selectBehavior;
58203 var _behaviors = [];
58205 mode.selectBehavior = function (val) {
58206 if (!arguments.length) return _selectBehavior;
58207 _selectBehavior = val;
58211 mode.enter = function () {
58212 if (!_behaviors.length) {
58213 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
58214 _behaviors = [behaviorPaste(context), behaviorHover(context).on('hover', context.ui().sidebar.hover), _selectBehavior, behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
58217 _behaviors.forEach(context.install); // Get focus on the body.
58220 if (document.activeElement && document.activeElement.blur) {
58221 document.activeElement.blur();
58225 context.ui().sidebar.show(sidebar);
58227 context.ui().sidebar.select(null);
58231 mode.exit = function () {
58232 context.ui().sidebar.hover.cancel();
58234 _behaviors.forEach(context.uninstall);
58237 context.ui().sidebar.hide();
58241 mode.sidebar = function (_) {
58242 if (!arguments.length) return sidebar;
58247 mode.operations = function () {
58248 return [operationPaste(context)];
58254 function behaviorAddWay(context) {
58255 var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
58256 var draw = behaviorDraw(context);
58258 function behavior(surface) {
58259 draw.on('click', function () {
58260 dispatch$1.apply('start', this, arguments);
58261 }).on('clickWay', function () {
58262 dispatch$1.apply('startFromWay', this, arguments);
58263 }).on('clickNode', function () {
58264 dispatch$1.apply('startFromNode', this, arguments);
58265 }).on('cancel', behavior.cancel).on('finish', behavior.cancel);
58266 context.map().dblclickZoomEnable(false);
58267 surface.call(draw);
58270 behavior.off = function (surface) {
58271 surface.call(draw.off);
58274 behavior.cancel = function () {
58275 window.setTimeout(function () {
58276 context.map().dblclickZoomEnable(true);
58278 context.enter(modeBrowse(context));
58281 return utilRebind(behavior, dispatch$1, 'on');
58284 function behaviorHash(context) {
58285 // cached window.location.hash
58286 var _cachedHash = null; // allowable latitude range
58288 var _latitudeLimit = 90 - 1e-8;
58290 function computedHashParameters() {
58291 var map = context.map();
58292 var center = map.center();
58293 var zoom = map.zoom();
58294 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
58295 var oldParams = utilObjectOmit(utilStringQs(window.location.hash), ['comment', 'source', 'hashtags', 'walkthrough']);
58296 var newParams = {};
58297 delete oldParams.id;
58298 var selected = context.selectedIDs().filter(function (id) {
58299 return context.hasEntity(id);
58302 if (selected.length) {
58303 newParams.id = selected.join(',');
58306 newParams.map = zoom.toFixed(2) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
58307 return Object.assign(oldParams, newParams);
58310 function computedHash() {
58311 return '#' + utilQsString(computedHashParameters(), true);
58314 function computedTitle(includeChangeCount) {
58315 var baseTitle = context.documentTitleBase() || 'iD';
58319 var selected = context.selectedIDs().filter(function (id) {
58320 return context.hasEntity(id);
58323 if (selected.length) {
58324 var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
58326 if (selected.length > 1) {
58327 contextual = _t('title.labeled_and_more', {
58328 labeled: firstLabel,
58329 count: selected.length - 1
58332 contextual = firstLabel;
58335 titleID = 'context';
58338 if (includeChangeCount) {
58339 changeCount = context.history().difference().summary().length;
58341 if (changeCount > 0) {
58342 titleID = contextual ? 'changes_context' : 'changes';
58347 return _t('title.format.' + titleID, {
58348 changes: changeCount,
58350 context: contextual
58357 function updateTitle(includeChangeCount) {
58358 if (!context.setsDocumentTitle()) return;
58359 var newTitle = computedTitle(includeChangeCount);
58361 if (document.title !== newTitle) {
58362 document.title = newTitle;
58366 function updateHashIfNeeded() {
58367 if (context.inIntro()) return;
58368 var latestHash = computedHash();
58370 if (_cachedHash !== latestHash) {
58371 _cachedHash = latestHash; // Update the URL hash without affecting the browser navigation stack,
58372 // though unavoidably creating a browser history entry
58374 window.history.replaceState(null, computedTitle(false
58375 /* includeChangeCount */
58376 ), latestHash); // set the title we want displayed for the browser tab/window
58379 /* includeChangeCount */
58384 var _throttledUpdate = throttle(updateHashIfNeeded, 500);
58386 var _throttledUpdateTitle = throttle(function () {
58388 /* includeChangeCount */
58392 function hashchange() {
58393 // ignore spurious hashchange events
58394 if (window.location.hash === _cachedHash) return;
58395 _cachedHash = window.location.hash;
58396 var q = utilStringQs(_cachedHash);
58397 var mapArgs = (q.map || '').split('/').map(Number);
58399 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
58400 // replace bogus hash
58401 updateHashIfNeeded();
58403 // don't update if the new hash already reflects the state of iD
58404 if (_cachedHash === computedHash()) return;
58405 var mode = context.mode();
58406 context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
58408 if (q.id && mode) {
58409 var ids = q.id.split(',').filter(function (id) {
58410 return context.hasEntity(id);
58413 if (ids.length && (mode.id === 'browse' || mode.id === 'select' && !utilArrayIdentical(mode.selectedIDs(), ids))) {
58414 context.enter(modeSelect(context, ids));
58419 var center = context.map().center();
58420 var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
58421 var maxdist = 500; // Don't allow the hash location to change too much while drawing
58422 // This can happen if the user accidentally hit the back button. #3996
58424 if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
58425 context.enter(modeBrowse(context));
58431 function behavior() {
58432 context.map().on('move.behaviorHash', _throttledUpdate);
58433 context.history().on('change.behaviorHash', _throttledUpdateTitle);
58434 context.on('enter.behaviorHash', _throttledUpdate);
58435 select(window).on('hashchange.behaviorHash', hashchange);
58437 if (window.location.hash) {
58438 var q = utilStringQs(window.location.hash);
58441 //if (!context.history().hasRestorableChanges()) {
58442 // targeting specific features: download, select, and zoom to them
58443 context.zoomToEntity(q.id.split(',')[0], !q.map); //}
58446 if (q.walkthrough === 'true') {
58447 behavior.startWalkthrough = true;
58451 behavior.hadHash = true;
58455 updateTitle(false);
58459 behavior.off = function () {
58460 _throttledUpdate.cancel();
58462 _throttledUpdateTitle.cancel();
58464 context.map().on('move.behaviorHash', null);
58465 context.on('enter.behaviorHash', null);
58466 select(window).on('hashchange.behaviorHash', null);
58467 window.location.hash = '';
58474 iD.coreDifference represents the difference between two graphs.
58475 It knows how to calculate the set of entities that were
58476 created, modified, or deleted, and also contains the logic
58477 for recursively extending a difference to the complete set
58478 of entities that will require a redraw, taking into account
58479 child and parent relationships.
58482 function coreDifference(base, head) {
58484 var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties'
58488 function checkEntityID(id) {
58489 var h = head.entities[id];
58490 var b = base.entities[id];
58491 if (h === b) return;
58492 if (_changes[id]) return;
58499 _didChange.deletion = true;
58508 _didChange.addition = true;
58513 if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
58518 _didChange.geometry = true;
58519 _didChange.properties = true;
58523 if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
58528 _didChange.geometry = true;
58531 if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
58536 _didChange.geometry = true;
58539 if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
58544 _didChange.properties = true;
58550 // HOT CODE: there can be many thousands of downloaded entities, so looping
58551 // through them all can become a performance bottleneck. Optimize by
58552 // resolving duplicates and using a basic `for` loop
58553 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
58555 for (var i = 0; i < ids.length; i++) {
58556 checkEntityID(ids[i]);
58562 _diff.length = function length() {
58563 return Object.keys(_changes).length;
58566 _diff.changes = function changes() {
58570 _diff.didChange = _didChange; // pass true to include affected relation members
58572 _diff.extantIDs = function extantIDs(includeRelMembers) {
58573 var result = new Set();
58574 Object.keys(_changes).forEach(function (id) {
58575 if (_changes[id].head) {
58579 var h = _changes[id].head;
58580 var b = _changes[id].base;
58581 var entity = h || b;
58583 if (includeRelMembers && entity.type === 'relation') {
58584 var mh = h ? h.members.map(function (m) {
58587 var mb = b ? b.members.map(function (m) {
58590 utilArrayUnion(mh, mb).forEach(function (memberID) {
58591 if (head.hasEntity(memberID)) {
58592 result.add(memberID);
58597 return Array.from(result);
58600 _diff.modified = function modified() {
58602 Object.values(_changes).forEach(function (change) {
58603 if (change.base && change.head) {
58604 result.push(change.head);
58610 _diff.created = function created() {
58612 Object.values(_changes).forEach(function (change) {
58613 if (!change.base && change.head) {
58614 result.push(change.head);
58620 _diff.deleted = function deleted() {
58622 Object.values(_changes).forEach(function (change) {
58623 if (change.base && !change.head) {
58624 result.push(change.base);
58630 _diff.summary = function summary() {
58632 var keys = Object.keys(_changes);
58634 for (var i = 0; i < keys.length; i++) {
58635 var change = _changes[keys[i]];
58637 if (change.head && change.head.geometry(head) !== 'vertex') {
58638 addEntity(change.head, head, change.base ? 'modified' : 'created');
58639 } else if (change.base && change.base.geometry(base) !== 'vertex') {
58640 addEntity(change.base, base, 'deleted');
58641 } else if (change.base && change.head) {
58643 var moved = !fastDeepEqual(change.base.loc, change.head.loc);
58644 var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
58647 addParents(change.head);
58650 if (retagged || moved && change.head.hasInterestingTags()) {
58651 addEntity(change.head, head, 'modified');
58653 } else if (change.head && change.head.hasInterestingTags()) {
58655 addEntity(change.head, head, 'created');
58656 } else if (change.base && change.base.hasInterestingTags()) {
58658 addEntity(change.base, base, 'deleted');
58662 return Object.values(relevant);
58664 function addEntity(entity, graph, changeType) {
58665 relevant[entity.id] = {
58668 changeType: changeType
58672 function addParents(entity) {
58673 var parents = head.parentWays(entity);
58675 for (var j = parents.length - 1; j >= 0; j--) {
58676 var parent = parents[j];
58678 if (!(parent.id in relevant)) {
58679 addEntity(parent, head, 'modified');
58683 }; // returns complete set of entities that require a redraw
58684 // (optionally within given `extent`)
58687 _diff.complete = function complete(extent) {
58691 for (id in _changes) {
58692 change = _changes[id];
58693 var h = change.head;
58694 var b = change.base;
58695 var entity = h || b;
58697 if (extent && (!h || !h.intersects(extent, head)) && (!b || !b.intersects(extent, base))) continue;
58700 if (entity.type === 'way') {
58701 var nh = h ? h.nodes : [];
58702 var nb = b ? b.nodes : [];
58704 diff = utilArrayDifference(nh, nb);
58706 for (i = 0; i < diff.length; i++) {
58707 result[diff[i]] = head.hasEntity(diff[i]);
58710 diff = utilArrayDifference(nb, nh);
58712 for (i = 0; i < diff.length; i++) {
58713 result[diff[i]] = head.hasEntity(diff[i]);
58717 if (entity.type === 'relation' && entity.isMultipolygon()) {
58718 var mh = h ? h.members.map(function (m) {
58721 var mb = b ? b.members.map(function (m) {
58724 var ids = utilArrayUnion(mh, mb);
58726 for (i = 0; i < ids.length; i++) {
58727 var member = head.hasEntity(ids[i]);
58728 if (!member) continue; // not downloaded
58730 if (extent && !member.intersects(extent, head)) continue; // not visible
58732 result[ids[i]] = member;
58736 addParents(head.parentWays(entity), result);
58737 addParents(head.parentRelations(entity), result);
58742 function addParents(parents, result) {
58743 for (var i = 0; i < parents.length; i++) {
58744 var parent = parents[i];
58745 if (parent.id in result) continue;
58746 result[parent.id] = parent;
58747 addParents(head.parentRelations(parent), result);
58755 function coreTree(head) {
58756 // tree for entities
58757 var _rtree = new RBush();
58759 var _bboxes = {}; // maintain a separate tree for granular way segments
58761 var _segmentsRTree = new RBush();
58763 var _segmentsBBoxes = {};
58764 var _segmentsByWayId = {};
58767 function entityBBox(entity) {
58768 var bbox = entity.extent(head).bbox();
58769 bbox.id = entity.id;
58770 _bboxes[entity.id] = bbox;
58774 function segmentBBox(segment) {
58775 var extent = segment.extent(head); // extent can be null if the node entities aren't in the graph for some reason
58777 if (!extent) return null;
58778 var bbox = extent.bbox();
58779 bbox.segment = segment;
58780 _segmentsBBoxes[segment.id] = bbox;
58784 function removeEntity(entity) {
58785 _rtree.remove(_bboxes[entity.id]);
58787 delete _bboxes[entity.id];
58789 if (_segmentsByWayId[entity.id]) {
58790 _segmentsByWayId[entity.id].forEach(function (segment) {
58791 _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
58793 delete _segmentsBBoxes[segment.id];
58796 delete _segmentsByWayId[entity.id];
58800 function loadEntities(entities) {
58801 _rtree.load(entities.map(entityBBox));
58804 entities.forEach(function (entity) {
58805 if (entity.segments) {
58806 var entitySegments = entity.segments(head); // cache these to make them easy to remove later
58808 _segmentsByWayId[entity.id] = entitySegments;
58809 segments = segments.concat(entitySegments);
58812 if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
58815 function updateParents(entity, insertions, memo) {
58816 head.parentWays(entity).forEach(function (way) {
58817 if (_bboxes[way.id]) {
58819 insertions[way.id] = way;
58822 updateParents(way, insertions, memo);
58824 head.parentRelations(entity).forEach(function (relation) {
58825 if (memo[entity.id]) return;
58826 memo[entity.id] = true;
58828 if (_bboxes[relation.id]) {
58829 removeEntity(relation);
58830 insertions[relation.id] = relation;
58833 updateParents(relation, insertions, memo);
58837 tree.rebase = function (entities, force) {
58838 var insertions = {};
58840 for (var i = 0; i < entities.length; i++) {
58841 var entity = entities[i];
58842 if (!entity.visible) continue;
58844 if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
58847 } else if (_bboxes[entity.id]) {
58848 removeEntity(entity);
58852 insertions[entity.id] = entity;
58853 updateParents(entity, insertions, {});
58856 loadEntities(Object.values(insertions));
58860 function updateToGraph(graph) {
58861 if (graph === head) return;
58862 var diff = coreDifference(head, graph);
58864 var changed = diff.didChange;
58865 if (!changed.addition && !changed.deletion && !changed.geometry) return;
58866 var insertions = {};
58868 if (changed.deletion) {
58869 diff.deleted().forEach(function (entity) {
58870 removeEntity(entity);
58874 if (changed.geometry) {
58875 diff.modified().forEach(function (entity) {
58876 removeEntity(entity);
58877 insertions[entity.id] = entity;
58878 updateParents(entity, insertions, {});
58882 if (changed.addition) {
58883 diff.created().forEach(function (entity) {
58884 insertions[entity.id] = entity;
58888 loadEntities(Object.values(insertions));
58889 } // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
58892 tree.intersects = function (extent, graph) {
58893 updateToGraph(graph);
58894 return _rtree.search(extent.bbox()).map(function (bbox) {
58895 return graph.entity(bbox.id);
58897 }; // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
58900 tree.waySegments = function (extent, graph) {
58901 updateToGraph(graph);
58902 return _segmentsRTree.search(extent.bbox()).map(function (bbox) {
58903 return bbox.segment;
58910 function uiModal(selection, blocking) {
58913 var keybinding = utilKeybinding('modal');
58914 var previous = selection.select('div.modal');
58915 var animate = previous.empty();
58916 previous.transition().duration(200).style('opacity', 0).remove();
58917 var shaded = selection.append('div').attr('class', 'shaded').style('opacity', 0);
58919 shaded.close = function () {
58920 shaded.transition().duration(200).style('opacity', 0).remove();
58921 modal.transition().duration(200).style('top', '0px');
58922 select(document).call(keybinding.unbind);
58925 var modal = shaded.append('div').attr('class', 'modal fillL');
58926 modal.append('input').attr('class', 'keytrap keytrap-first').on('focus.keytrap', moveFocusToLast);
58929 shaded.on('click.remove-modal', function (d3_event) {
58930 if (d3_event.target === _this) {
58934 modal.append('button').attr('class', 'close').on('click', shaded.close).call(svgIcon('#iD-icon-close'));
58935 keybinding.on('⌫', shaded.close).on('⎋', shaded.close);
58936 select(document).call(keybinding);
58939 modal.append('div').attr('class', 'content');
58940 modal.append('input').attr('class', 'keytrap keytrap-last').on('focus.keytrap', moveFocusToFirst);
58943 shaded.transition().style('opacity', 1);
58945 shaded.style('opacity', 1);
58950 function moveFocusToFirst() {
58951 var node = modal // there are additional rules about what's focusable, but this suits our purposes
58952 .select('a, button, input:not(.keytrap), select, textarea').node();
58957 select(this).node().blur();
58961 function moveFocusToLast() {
58962 var nodes = modal.selectAll('a, button, input:not(.keytrap), select, textarea').nodes();
58964 if (nodes.length) {
58965 nodes[nodes.length - 1].focus();
58967 select(this).node().blur();
58972 function uiLoading(context) {
58973 var _modalSelection = select(null);
58976 var _blocking = false;
58978 var loading = function loading(selection) {
58979 _modalSelection = uiModal(selection, _blocking);
58981 var loadertext = _modalSelection.select('.content').classed('loading-modal', true).append('div').attr('class', 'modal-section fillL');
58983 loadertext.append('img').attr('class', 'loader').attr('src', context.imagePath('loader-white.gif'));
58984 loadertext.append('h3').html(_message);
58986 _modalSelection.select('button.close').attr('class', 'hide');
58991 loading.message = function (val) {
58992 if (!arguments.length) return _message;
58997 loading.blocking = function (val) {
58998 if (!arguments.length) return _blocking;
59003 loading.close = function () {
59004 _modalSelection.remove();
59007 loading.isShown = function () {
59008 return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
59014 function coreHistory(context) {
59015 var dispatch$1 = dispatch('reset', 'change', 'merge', 'restore', 'undone', 'redone');
59017 var _lock = utilSessionMutex('lock'); // restorable if iD not open in another window/tab and a saved history exists in localStorage
59020 var _hasUnresolvedRestorableChanges = _lock.lock() && !!corePreferences(getKey('saved_history'));
59022 var duration = 150;
59023 var _imageryUsed = [];
59024 var _photoOverlaysUsed = [];
59025 var _checkpoints = {};
59033 var _tree; // internal _act, accepts list of actions and eased time
59036 function _act(actions, t) {
59037 actions = Array.prototype.slice.call(actions);
59040 if (typeof actions[actions.length - 1] !== 'function') {
59041 annotation = actions.pop();
59044 var graph = _stack[_index].graph;
59046 for (var i = 0; i < actions.length; i++) {
59047 graph = actions[i](graph, t);
59052 annotation: annotation,
59053 imageryUsed: _imageryUsed,
59054 photoOverlaysUsed: _photoOverlaysUsed,
59055 transform: context.projection.transform(),
59056 selectedIDs: context.selectedIDs()
59058 } // internal _perform with eased time
59061 function _perform(args, t) {
59062 var previous = _stack[_index].graph;
59063 _stack = _stack.slice(0, _index + 1);
59065 var actionResult = _act(args, t);
59067 _stack.push(actionResult);
59070 return change(previous);
59071 } // internal _replace with eased time
59074 function _replace(args, t) {
59075 var previous = _stack[_index].graph; // assert(_index == _stack.length - 1)
59077 var actionResult = _act(args, t);
59079 _stack[_index] = actionResult;
59080 return change(previous);
59081 } // internal _overwrite with eased time
59084 function _overwrite(args, t) {
59085 var previous = _stack[_index].graph;
59093 _stack = _stack.slice(0, _index + 1);
59095 var actionResult = _act(args, t);
59097 _stack.push(actionResult);
59100 return change(previous);
59101 } // determine difference and dispatch a change event
59104 function change(previous) {
59105 var difference = coreDifference(previous, history.graph());
59107 if (!_pausedGraph) {
59108 dispatch$1.call('change', this, difference);
59112 } // iD uses namespaced keys so multiple installations do not conflict
59115 function getKey(n) {
59116 return 'iD_' + window.location.origin + '_' + n;
59120 graph: function graph() {
59121 return _stack[_index].graph;
59123 tree: function tree() {
59126 base: function base() {
59127 return _stack[0].graph;
59129 merge: function merge(entities
59132 var stack = _stack.map(function (state) {
59133 return state.graph;
59136 _stack[0].graph.rebase(entities, stack, false);
59138 _tree.rebase(entities, false);
59140 dispatch$1.call('merge', this, entities);
59142 perform: function perform() {
59143 // complete any transition already in progress
59144 select(document).interrupt('history.perform');
59145 var transitionable = false;
59146 var action0 = arguments[0];
59148 if (arguments.length === 1 || arguments.length === 2 && typeof arguments[1] !== 'function') {
59149 transitionable = !!action0.transitionable;
59152 if (transitionable) {
59153 var origArguments = arguments;
59154 select(document).transition('history.perform').duration(duration).ease(linear$1).tween('history.tween', function () {
59155 return function (t) {
59156 if (t < 1) _overwrite([action0], t);
59158 }).on('start', function () {
59159 _perform([action0], 0);
59160 }).on('end interrupt', function () {
59161 _overwrite(origArguments, 1);
59164 return _perform(arguments);
59167 replace: function replace() {
59168 select(document).interrupt('history.perform');
59169 return _replace(arguments, 1);
59171 // Same as calling pop and then perform
59172 overwrite: function overwrite() {
59173 select(document).interrupt('history.perform');
59174 return _overwrite(arguments, 1);
59176 pop: function pop(n) {
59177 select(document).interrupt('history.perform');
59178 var previous = _stack[_index].graph;
59180 if (isNaN(+n) || +n < 0) {
59184 while (n-- > 0 && _index > 0) {
59190 return change(previous);
59192 // Back to the previous annotated state or _index = 0.
59193 undo: function undo() {
59194 select(document).interrupt('history.perform');
59195 var previousStack = _stack[_index];
59196 var previous = previousStack.graph;
59198 while (_index > 0) {
59200 if (_stack[_index].annotation) break;
59203 dispatch$1.call('undone', this, _stack[_index], previousStack);
59204 return change(previous);
59206 // Forward to the next annotated state.
59207 redo: function redo() {
59208 select(document).interrupt('history.perform');
59209 var previousStack = _stack[_index];
59210 var previous = previousStack.graph;
59211 var tryIndex = _index;
59213 while (tryIndex < _stack.length - 1) {
59216 if (_stack[tryIndex].annotation) {
59218 dispatch$1.call('redone', this, _stack[_index], previousStack);
59223 return change(previous);
59225 pauseChangeDispatch: function pauseChangeDispatch() {
59226 if (!_pausedGraph) {
59227 _pausedGraph = _stack[_index].graph;
59230 resumeChangeDispatch: function resumeChangeDispatch() {
59231 if (_pausedGraph) {
59232 var previous = _pausedGraph;
59233 _pausedGraph = null;
59234 return change(previous);
59237 undoAnnotation: function undoAnnotation() {
59241 if (_stack[i].annotation) return _stack[i].annotation;
59245 redoAnnotation: function redoAnnotation() {
59246 var i = _index + 1;
59248 while (i <= _stack.length - 1) {
59249 if (_stack[i].annotation) return _stack[i].annotation;
59253 // Returns the entities from the active graph with bounding boxes
59254 // overlapping the given `extent`.
59255 intersects: function intersects(extent) {
59256 return _tree.intersects(extent, _stack[_index].graph);
59258 difference: function difference() {
59259 var base = _stack[0].graph;
59260 var head = _stack[_index].graph;
59261 return coreDifference(base, head);
59263 changes: function changes(action) {
59264 var base = _stack[0].graph;
59265 var head = _stack[_index].graph;
59268 head = action(head);
59271 var difference = coreDifference(base, head);
59273 modified: difference.modified(),
59274 created: difference.created(),
59275 deleted: difference.deleted()
59278 hasChanges: function hasChanges() {
59279 return this.difference().length() > 0;
59281 imageryUsed: function imageryUsed(sources) {
59283 _imageryUsed = sources;
59288 _stack.slice(1, _index + 1).forEach(function (state) {
59289 state.imageryUsed.forEach(function (source) {
59290 if (source !== 'Custom') {
59296 return Array.from(s);
59299 photoOverlaysUsed: function photoOverlaysUsed(sources) {
59301 _photoOverlaysUsed = sources;
59306 _stack.slice(1, _index + 1).forEach(function (state) {
59307 if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
59308 state.photoOverlaysUsed.forEach(function (photoOverlay) {
59309 s.add(photoOverlay);
59314 return Array.from(s);
59317 // save the current history state
59318 checkpoint: function checkpoint(key) {
59319 _checkpoints[key] = {
59325 // restore history state to a given checkpoint or reset completely
59326 reset: function reset(key) {
59327 if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
59328 _stack = _checkpoints[key].stack;
59329 _index = _checkpoints[key].index;
59335 _tree = coreTree(_stack[0].graph);
59339 dispatch$1.call('reset');
59340 dispatch$1.call('change');
59343 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
59346 // 1. Start the walkthrough.
59347 // 2. Get to a "free editing" tutorial step
59348 // 3. Make your edits to the walkthrough map
59349 // 4. In your browser dev console run:
59350 // `id.history().toIntroGraph()`
59351 // 5. This outputs stringified JSON to the browser console
59352 // 6. Copy it to `data/intro_graph.json` and prettify it in your code editor
59353 toIntroGraph: function toIntroGraph() {
59360 var graph = this.graph();
59361 var baseEntities = {}; // clone base entities..
59363 Object.values(graph.base().entities).forEach(function (entity) {
59364 var copy = copyIntroEntity(entity);
59365 baseEntities[copy.id] = copy;
59366 }); // replace base entities with head entities..
59368 Object.keys(graph.entities).forEach(function (id) {
59369 var entity = graph.entities[id];
59372 var copy = copyIntroEntity(entity);
59373 baseEntities[copy.id] = copy;
59375 delete baseEntities[id];
59377 }); // swap temporary for permanent ids..
59379 Object.values(baseEntities).forEach(function (entity) {
59380 if (Array.isArray(entity.nodes)) {
59381 entity.nodes = entity.nodes.map(function (node) {
59382 return permIDs[node] || node;
59386 if (Array.isArray(entity.members)) {
59387 entity.members = entity.members.map(function (member) {
59388 member.id = permIDs[member.id] || member.id;
59393 return JSON.stringify({
59394 dataIntroGraph: baseEntities
59397 function copyIntroEntity(source) {
59398 var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); // Note: the copy is no longer an osmEntity, so it might not have `tags`
59400 if (copy.tags && !Object.keys(copy.tags)) {
59404 if (Array.isArray(copy.loc)) {
59405 copy.loc[0] = +copy.loc[0].toFixed(6);
59406 copy.loc[1] = +copy.loc[1].toFixed(6);
59409 var match = source.id.match(/([nrw])-\d*/); // temporary id
59411 if (match !== null) {
59412 var nrw = match[1];
59416 permID = nrw + ++nextID[nrw];
59417 } while (baseEntities.hasOwnProperty(permID));
59419 copy.id = permIDs[source.id] = permID;
59425 toJSON: function toJSON() {
59426 if (!this.hasChanges()) return;
59427 var allEntities = {};
59428 var baseEntities = {};
59429 var base = _stack[0];
59431 var s = _stack.map(function (i) {
59434 Object.keys(i.graph.entities).forEach(function (id) {
59435 var entity = i.graph.entities[id];
59438 var key = osmEntity.key(entity);
59439 allEntities[key] = entity;
59440 modified.push(key);
59443 } // make sure that the originals of changed or deleted entities get merged
59444 // into the base of the _stack after restoring the data from JSON.
59447 if (id in base.graph.entities) {
59448 baseEntities[id] = base.graph.entities[id];
59451 if (entity && entity.nodes) {
59452 // get originals of pre-existing child nodes
59453 entity.nodes.forEach(function (nodeID) {
59454 if (nodeID in base.graph.entities) {
59455 baseEntities[nodeID] = base.graph.entities[nodeID];
59458 } // get originals of parent entities too
59461 var baseParents = base.graph._parentWays[id];
59464 baseParents.forEach(function (parentID) {
59465 if (parentID in base.graph.entities) {
59466 baseEntities[parentID] = base.graph.entities[parentID];
59472 if (modified.length) x.modified = modified;
59473 if (deleted.length) x.deleted = deleted;
59474 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
59475 if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
59476 if (i.annotation) x.annotation = i.annotation;
59477 if (i.transform) x.transform = i.transform;
59478 if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
59482 return JSON.stringify({
59484 entities: Object.values(allEntities),
59485 baseEntities: Object.values(baseEntities),
59487 nextIDs: osmEntity.id.next,
59489 // note the time the changes were saved
59490 timestamp: new Date().getTime()
59493 fromJSON: function fromJSON(json, loadChildNodes) {
59494 var h = JSON.parse(json);
59495 var loadComplete = true;
59496 osmEntity.id.next = h.nextIDs;
59499 if (h.version === 2 || h.version === 3) {
59500 var allEntities = {};
59501 h.entities.forEach(function (entity) {
59502 allEntities[osmEntity.key(entity)] = osmEntity(entity);
59505 if (h.version === 3) {
59506 // This merges originals for changed entities into the base of
59507 // the _stack even if the current _stack doesn't have them (for
59508 // example when iD has been restarted in a different region)
59509 var baseEntities = h.baseEntities.map(function (d) {
59510 return osmEntity(d);
59513 var stack = _stack.map(function (state) {
59514 return state.graph;
59517 _stack[0].graph.rebase(baseEntities, stack, true);
59519 _tree.rebase(baseEntities, true); // When we restore a modified way, we also need to fetch any missing
59520 // childnodes that would normally have been downloaded with it.. #2142
59523 if (loadChildNodes) {
59524 var osm = context.connection();
59525 var baseWays = baseEntities.filter(function (e) {
59526 return e.type === 'way';
59528 var nodeIDs = baseWays.reduce(function (acc, way) {
59529 return utilArrayUnion(acc, way.nodes);
59531 var missing = nodeIDs.filter(function (n) {
59532 return !_stack[0].graph.hasEntity(n);
59535 if (missing.length && osm) {
59536 loadComplete = false;
59537 context.map().redrawEnable(false);
59538 var loading = uiLoading(context).blocking(true);
59539 context.container().call(loading);
59541 var childNodesLoaded = function childNodesLoaded(err, result) {
59543 var visibleGroups = utilArrayGroupBy(result.data, 'visible');
59544 var visibles = visibleGroups["true"] || []; // alive nodes
59546 var invisibles = visibleGroups["false"] || []; // deleted nodes
59548 if (visibles.length) {
59549 var visibleIDs = visibles.map(function (entity) {
59553 var stack = _stack.map(function (state) {
59554 return state.graph;
59557 missing = utilArrayDifference(missing, visibleIDs);
59559 _stack[0].graph.rebase(visibles, stack, true);
59561 _tree.rebase(visibles, true);
59562 } // fetch older versions of nodes that were deleted..
59565 invisibles.forEach(function (entity) {
59566 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
59570 if (err || !missing.length) {
59572 context.map().redrawEnable(true);
59573 dispatch$1.call('change');
59574 dispatch$1.call('restore', this);
59578 osm.loadMultiple(missing, childNodesLoaded);
59583 _stack = h.stack.map(function (d) {
59588 d.modified.forEach(function (key) {
59589 entity = allEntities[key];
59590 entities[entity.id] = entity;
59595 d.deleted.forEach(function (id) {
59596 entities[id] = undefined;
59601 graph: coreGraph(_stack[0].graph).load(entities),
59602 annotation: d.annotation,
59603 imageryUsed: d.imageryUsed,
59604 photoOverlaysUsed: d.photoOverlaysUsed,
59605 transform: d.transform,
59606 selectedIDs: d.selectedIDs
59610 // original version
59611 _stack = h.stack.map(function (d) {
59614 for (var i in d.entities) {
59615 var entity = d.entities[i];
59616 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
59619 d.graph = coreGraph(_stack[0].graph).load(entities);
59624 var transform = _stack[_index].transform;
59627 context.map().transformEase(transform, 0); // 0 = immediate, no easing
59630 if (loadComplete) {
59631 dispatch$1.call('change');
59632 dispatch$1.call('restore', this);
59637 lock: function lock() {
59638 return _lock.lock();
59640 unlock: function unlock() {
59643 save: function save() {
59644 if (_lock.locked() && // don't overwrite existing, unresolved changes
59645 !_hasUnresolvedRestorableChanges) {
59646 corePreferences(getKey('saved_history'), history.toJSON() || null);
59651 // delete the history version saved in localStorage
59652 clearSaved: function clearSaved() {
59653 context.debouncedSave.cancel();
59655 if (_lock.locked()) {
59656 _hasUnresolvedRestorableChanges = false;
59657 corePreferences(getKey('saved_history'), null); // clear the changeset metadata associated with the saved history
59659 corePreferences('comment', null);
59660 corePreferences('hashtags', null);
59661 corePreferences('source', null);
59666 savedHistoryJSON: function savedHistoryJSON() {
59667 return corePreferences(getKey('saved_history'));
59669 hasRestorableChanges: function hasRestorableChanges() {
59670 return _hasUnresolvedRestorableChanges;
59672 // load history from a version stored in localStorage
59673 restore: function restore() {
59674 if (_lock.locked()) {
59675 _hasUnresolvedRestorableChanges = false;
59676 var json = this.savedHistoryJSON();
59677 if (json) history.fromJSON(json, true);
59683 return utilRebind(history, dispatch$1, 'on');
59687 * Look for roads that can be connected to other roads with a short extension
59690 function validationAlmostJunction(context) {
59691 var type = 'almost_junction';
59692 var EXTEND_TH_METERS = 5;
59693 var WELD_TH_METERS = 0.75; // Comes from considering bounding case of parallel ways
59695 var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; // Comes from considering bounding case of perpendicular ways
59697 var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
59699 function isHighway(entity) {
59700 return entity.type === 'way' && osmRoutableHighwayTagValues[entity.tags.highway];
59703 function isTaggedAsNotContinuing(node) {
59704 return node.tags.noexit === 'yes' || node.tags.amenity === 'parking_entrance' || node.tags.entrance && node.tags.entrance !== 'no';
59707 var validation = function checkAlmostJunction(entity, graph) {
59708 if (!isHighway(entity)) return [];
59709 if (entity.isDegenerate()) return [];
59710 var tree = context.history().tree();
59711 var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
59713 extendableNodeInfos.forEach(function (extendableNodeInfo) {
59714 issues.push(new validationIssue({
59716 subtype: 'highway-highway',
59717 severity: 'warning',
59718 message: function message(context) {
59719 var entity1 = context.hasEntity(this.entityIds[0]);
59721 if (this.entityIds[0] === this.entityIds[2]) {
59722 return entity1 ? _t.html('issues.almost_junction.self.message', {
59723 feature: utilDisplayLabel(entity1, context.graph())
59726 var entity2 = context.hasEntity(this.entityIds[2]);
59727 return entity1 && entity2 ? _t.html('issues.almost_junction.message', {
59728 feature: utilDisplayLabel(entity1, context.graph()),
59729 feature2: utilDisplayLabel(entity2, context.graph())
59733 reference: showReference,
59734 entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid],
59735 loc: extendableNodeInfo.node.loc,
59736 hash: JSON.stringify(extendableNodeInfo.node.loc),
59738 midId: extendableNodeInfo.mid.id,
59739 edge: extendableNodeInfo.edge,
59740 cross_loc: extendableNodeInfo.cross_loc
59742 dynamicFixes: makeFixes
59747 function makeFixes(context) {
59748 var fixes = [new validationIssueFix({
59749 icon: 'iD-icon-abutment',
59750 title: _t.html('issues.fix.connect_features.title'),
59751 onClick: function onClick(context) {
59752 var annotation = _t('issues.fix.connect_almost_junction.annotation');
59754 var _this$issue$entityIds = _slicedToArray(this.issue.entityIds, 3),
59755 endNodeId = _this$issue$entityIds[1],
59756 crossWayId = _this$issue$entityIds[2];
59758 var midNode = context.entity(this.issue.data.midId);
59759 var endNode = context.entity(endNodeId);
59760 var crossWay = context.entity(crossWayId); // When endpoints are close, just join if resulting small change in angle (#7201)
59762 var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
59764 if (nearEndNodes.length > 0) {
59765 var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
59768 context.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation);
59773 var targetEdge = this.issue.data.edge;
59774 var crossLoc = this.issue.data.cross_loc;
59775 var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
59776 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); // already a point nearby, just connect to that
59778 if (closestNodeInfo.distance < WELD_TH_METERS) {
59779 context.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation); // else add the end node to the edge way
59781 context.perform(actionAddMidpoint({
59784 }, endNode), annotation);
59788 var node = context.hasEntity(this.entityIds[1]);
59790 if (node && !node.hasInterestingTags()) {
59791 // node has no descriptive tags, suggest noexit fix
59792 fixes.push(new validationIssueFix({
59793 icon: 'maki-barrier',
59794 title: _t.html('issues.fix.tag_as_disconnected.title'),
59795 onClick: function onClick(context) {
59796 var nodeID = this.issue.entityIds[1];
59797 var tags = Object.assign({}, context.entity(nodeID).tags);
59798 tags.noexit = 'yes';
59799 context.perform(actionChangeTags(nodeID, tags), _t('issues.fix.tag_as_disconnected.annotation'));
59807 function showReference(selection) {
59808 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.almost_junction.highway-highway.reference'));
59811 function isExtendableCandidate(node, way) {
59812 // can not accurately test vertices on tiles not downloaded from osm - #5938
59813 var osm = services.osm;
59815 if (osm && !osm.isDataLoaded(node.loc)) {
59819 if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
59823 var occurrences = 0;
59825 for (var index in way.nodes) {
59826 if (way.nodes[index] === node.id) {
59829 if (occurrences > 1) {
59838 function findConnectableEndNodesByExtension(way) {
59840 if (way.isClosed()) return results;
59842 var indices = [0, way.nodes.length - 1];
59843 indices.forEach(function (nodeIndex) {
59844 var nodeID = way.nodes[nodeIndex];
59845 var node = graph.entity(nodeID);
59846 if (!isExtendableCandidate(node, way)) return;
59847 var connectionInfo = canConnectByExtend(way, nodeIndex);
59848 if (!connectionInfo) return;
59849 testNodes = graph.childNodes(way).slice(); // shallow copy
59851 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); // don't flag issue if connecting the ways would cause self-intersection
59853 if (geoHasSelfIntersections(testNodes, nodeID)) return;
59854 results.push(connectionInfo);
59859 function findNearbyEndNodes(node, way) {
59860 return [way.nodes[0], way.nodes[way.nodes.length - 1]].map(function (d) {
59861 return graph.entity(d);
59862 }).filter(function (d) {
59863 // Node cannot be near to itself, but other endnode of same way could be
59864 return d.id !== node.id && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
59868 function findSmallJoinAngle(midNode, tipNode, endNodes) {
59869 // Both nodes could be close, so want to join whichever is closest to collinear
59871 var minAngle = Infinity; // Checks midNode -> tipNode -> endNode for collinearity
59873 endNodes.forEach(function (endNode) {
59874 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
59875 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
59876 var diff = Math.max(a1, a2) - Math.min(a1, a2);
59878 if (diff < minAngle) {
59883 /* Threshold set by considering right angle triangle
59884 based on node joining threshold and extension distance */
59886 if (minAngle <= SIG_ANGLE_TH) return joinTo;
59890 function hasTag(tags, key) {
59891 return tags[key] !== undefined && tags[key] !== 'no';
59894 function canConnectWays(way, way2) {
59895 // allow self-connections
59896 if (way.id === way2.id) return true; // if one is bridge or tunnel, both must be bridge or tunnel
59898 if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
59899 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
59901 var layer1 = way.tags.layer || '0',
59902 layer2 = way2.tags.layer || '0';
59903 if (layer1 !== layer2) return false;
59904 var level1 = way.tags.level || '0',
59905 level2 = way2.tags.level || '0';
59906 if (level1 !== level2) return false;
59910 function canConnectByExtend(way, endNodeIdx) {
59911 var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
59913 var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
59915 var tipNode = graph.entity(tipNid);
59916 var midNode = graph.entity(midNid);
59917 var lon = tipNode.loc[0];
59918 var lat = tipNode.loc[1];
59919 var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
59920 var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
59921 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
59923 var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
59924 var t = EXTEND_TH_METERS / edgeLen + 1.0;
59925 var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
59927 var segmentInfos = tree.waySegments(queryExtent, graph);
59929 for (var i = 0; i < segmentInfos.length; i++) {
59930 var segmentInfo = segmentInfos[i];
59931 var way2 = graph.entity(segmentInfo.wayId);
59932 if (!isHighway(way2)) continue;
59933 if (!canConnectWays(way, way2)) continue;
59934 var nAid = segmentInfo.nodes[0],
59935 nBid = segmentInfo.nodes[1];
59936 if (nAid === tipNid || nBid === tipNid) continue;
59937 var nA = graph.entity(nAid),
59938 nB = graph.entity(nBid);
59939 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
59946 edge: [nA.id, nB.id],
59947 cross_loc: crossLoc
59956 validation.type = type;
59960 function validationCloseNodes(context) {
59961 var type = 'close_nodes';
59962 var pointThresholdMeters = 0.2;
59964 var validation = function validation(entity, graph) {
59965 if (entity.type === 'node') {
59966 return getIssuesForNode(entity);
59967 } else if (entity.type === 'way') {
59968 return getIssuesForWay(entity);
59973 function getIssuesForNode(node) {
59974 var parentWays = graph.parentWays(node);
59976 if (parentWays.length) {
59977 return getIssuesForVertex(node, parentWays);
59979 return getIssuesForDetachedPoint(node);
59983 function wayTypeFor(way) {
59984 if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
59985 if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
59986 if (way.tags.building && way.tags.building !== 'no' || way.tags['building:part'] && way.tags['building:part'] !== 'no') return 'building';
59987 if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
59988 var parentRelations = graph.parentRelations(way);
59990 for (var i in parentRelations) {
59991 var relation = parentRelations[i];
59992 if (relation.tags.type === 'boundary') return 'boundary';
59994 if (relation.isMultipolygon()) {
59995 if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
59996 if (relation.tags.building && relation.tags.building !== 'no' || relation.tags['building:part'] && relation.tags['building:part'] !== 'no') return 'building';
60003 function shouldCheckWay(way) {
60004 // don't flag issues where merging would create degenerate ways
60005 if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4) return false;
60006 var bbox = way.extent(graph).bbox();
60007 var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); // don't flag close nodes in very small ways
60009 if (hypotenuseMeters < 1.5) return false;
60013 function getIssuesForWay(way) {
60014 if (!shouldCheckWay(way)) return [];
60016 nodes = graph.childNodes(way);
60018 for (var i = 0; i < nodes.length - 1; i++) {
60019 var node1 = nodes[i];
60020 var node2 = nodes[i + 1];
60021 var issue = getWayIssueIfAny(node1, node2, way);
60022 if (issue) issues.push(issue);
60028 function getIssuesForVertex(node, parentWays) {
60031 function checkForCloseness(node1, node2, way) {
60032 var issue = getWayIssueIfAny(node1, node2, way);
60033 if (issue) issues.push(issue);
60036 for (var i = 0; i < parentWays.length; i++) {
60037 var parentWay = parentWays[i];
60038 if (!shouldCheckWay(parentWay)) continue;
60039 var lastIndex = parentWay.nodes.length - 1;
60041 for (var j = 0; j < parentWay.nodes.length; j++) {
60043 if (parentWay.nodes[j - 1] === node.id) {
60044 checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
60048 if (j !== lastIndex) {
60049 if (parentWay.nodes[j + 1] === node.id) {
60050 checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
60059 function thresholdMetersForWay(way) {
60060 if (!shouldCheckWay(way)) return 0;
60061 var wayType = wayTypeFor(way); // don't flag boundaries since they might be highly detailed and can't be easily verified
60063 if (wayType === 'boundary') return 0; // expect some features to be mapped with higher levels of detail
60065 if (wayType === 'indoor') return 0.01;
60066 if (wayType === 'building') return 0.05;
60067 if (wayType === 'path') return 0.1;
60071 function getIssuesForDetachedPoint(node) {
60073 var lon = node.loc[0];
60074 var lat = node.loc[1];
60075 var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
60076 var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
60077 var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]);
60078 var intersected = context.history().tree().intersects(queryExtent, graph);
60080 for (var j = 0; j < intersected.length; j++) {
60081 var nearby = intersected[j];
60082 if (nearby.id === node.id) continue;
60083 if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
60085 if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
60086 // allow very close points if tags indicate the z-axis might vary
60090 'addr:housenumber': true,
60093 var zAxisDifferentiates = false;
60095 for (var key in zAxisKeys) {
60096 var nodeValue = node.tags[key] || '0';
60097 var nearbyValue = nearby.tags[key] || '0';
60099 if (nodeValue !== nearbyValue) {
60100 zAxisDifferentiates = true;
60105 if (zAxisDifferentiates) continue;
60106 issues.push(new validationIssue({
60108 subtype: 'detached',
60109 severity: 'warning',
60110 message: function message(context) {
60111 var entity = context.hasEntity(this.entityIds[0]),
60112 entity2 = context.hasEntity(this.entityIds[1]);
60113 return entity && entity2 ? _t.html('issues.close_nodes.detached.message', {
60114 feature: utilDisplayLabel(entity, context.graph()),
60115 feature2: utilDisplayLabel(entity2, context.graph())
60118 reference: showReference,
60119 entityIds: [node.id, nearby.id],
60120 dynamicFixes: function dynamicFixes() {
60121 return [new validationIssueFix({
60122 icon: 'iD-operation-disconnect',
60123 title: _t.html('issues.fix.move_points_apart.title')
60124 }), new validationIssueFix({
60125 icon: 'iD-icon-layers',
60126 title: _t.html('issues.fix.use_different_layers_or_levels.title')
60135 function showReference(selection) {
60136 var referenceText = _t('issues.close_nodes.detached.reference');
60137 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
60141 function getWayIssueIfAny(node1, node2, way) {
60142 if (node1.id === node2.id || node1.hasInterestingTags() && node2.hasInterestingTags()) {
60146 if (node1.loc !== node2.loc) {
60147 var parentWays1 = graph.parentWays(node1);
60148 var parentWays2 = new Set(graph.parentWays(node2));
60149 var sharedWays = parentWays1.filter(function (parentWay) {
60150 return parentWays2.has(parentWay);
60152 var thresholds = sharedWays.map(function (parentWay) {
60153 return thresholdMetersForWay(parentWay);
60155 var threshold = Math.min.apply(Math, _toConsumableArray(thresholds));
60156 var distance = geoSphericalDistance(node1.loc, node2.loc);
60157 if (distance > threshold) return null;
60160 return new validationIssue({
60162 subtype: 'vertices',
60163 severity: 'warning',
60164 message: function message(context) {
60165 var entity = context.hasEntity(this.entityIds[0]);
60166 return entity ? _t.html('issues.close_nodes.message', {
60167 way: utilDisplayLabel(entity, context.graph())
60170 reference: showReference,
60171 entityIds: [way.id, node1.id, node2.id],
60173 dynamicFixes: function dynamicFixes() {
60174 return [new validationIssueFix({
60175 icon: 'iD-icon-plus',
60176 title: _t.html('issues.fix.merge_points.title'),
60177 onClick: function onClick(context) {
60178 var entityIds = this.issue.entityIds;
60179 var action = actionMergeNodes([entityIds[1], entityIds[2]]);
60180 context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
60182 }), new validationIssueFix({
60183 icon: 'iD-operation-disconnect',
60184 title: _t.html('issues.fix.move_points_apart.title')
60189 function showReference(selection) {
60190 var referenceText = _t('issues.close_nodes.reference');
60191 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
60196 validation.type = type;
60200 function validationCrossingWays(context) {
60201 var type = 'crossing_ways'; // returns the way or its parent relation, whichever has a useful feature type
60203 function getFeatureWithFeatureTypeTagsForWay(way, graph) {
60204 if (getFeatureType(way, graph) === null) {
60205 // if the way doesn't match a feature type, check its parent relations
60206 var parentRels = graph.parentRelations(way);
60208 for (var i = 0; i < parentRels.length; i++) {
60209 var rel = parentRels[i];
60211 if (getFeatureType(rel, graph) !== null) {
60220 function hasTag(tags, key) {
60221 return tags[key] !== undefined && tags[key] !== 'no';
60224 function taggedAsIndoor(tags) {
60225 return hasTag(tags, 'indoor') || hasTag(tags, 'level') || tags.highway === 'corridor';
60228 function allowsBridge(featureType) {
60229 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
60232 function allowsTunnel(featureType) {
60233 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
60237 var ignoredBuildings = {
60244 function getFeatureType(entity, graph) {
60245 var geometry = entity.geometry(graph);
60246 if (geometry !== 'line' && geometry !== 'area') return null;
60247 var tags = entity.tags;
60248 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
60249 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; // don't check railway or waterway areas
60251 if (geometry !== 'line') return null;
60252 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
60253 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
60257 function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
60258 // assume 0 by default
60259 var level1 = tags1.level || '0';
60260 var level2 = tags2.level || '0';
60262 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
60263 // assume features don't interact if they're indoor on different levels
60265 } // assume 0 by default; don't use way.layer() since we account for structures here
60268 var layer1 = tags1.layer || '0';
60269 var layer2 = tags2.layer || '0';
60271 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
60272 if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
60273 if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; // crossing bridges must use different layers
60275 if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
60276 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
60278 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
60279 if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
60280 if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; // crossing tunnels must use different layers
60282 if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
60283 } 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
60286 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
60287 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
60289 if (featureType1 === 'building' || featureType2 === 'building') {
60290 // for building crossings, different layers are enough
60291 if (layer1 !== layer2) return true;
60295 } // highway values for which we shouldn't recommend connecting to waterways
60298 var highwaysDisallowingFords = {
60300 motorway_link: true,
60304 primary_link: true,
60306 secondary_link: true
60308 var nonCrossingHighways = {
60312 function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
60313 var featureType1 = getFeatureType(entity1, graph);
60314 var featureType2 = getFeatureType(entity2, graph);
60315 var geometry1 = entity1.geometry(graph);
60316 var geometry2 = entity2.geometry(graph);
60317 var bothLines = geometry1 === 'line' && geometry2 === 'line';
60319 if (featureType1 === featureType2) {
60320 if (featureType1 === 'highway') {
60321 var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
60322 var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
60324 if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
60325 // one feature is a path but not both
60326 var roadFeature = entity1IsPath ? entity2 : entity1;
60328 if (nonCrossingHighways[roadFeature.tags.highway]) {
60329 // don't mark path connections with certain roads as crossings
60333 var pathFeature = entity1IsPath ? entity1 : entity2;
60335 if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
60336 // if the path is a crossing, match the crossing type
60337 return bothLines ? {
60338 highway: 'crossing',
60339 crossing: pathFeature.tags.crossing
60341 } // don't add a `crossing` subtag to ambiguous crossings
60344 return bothLines ? {
60345 highway: 'crossing'
60352 if (featureType1 === 'waterway') return {};
60353 if (featureType1 === 'railway') return {};
60355 var featureTypes = [featureType1, featureType2];
60357 if (featureTypes.indexOf('highway') !== -1) {
60358 if (featureTypes.indexOf('railway') !== -1) {
60359 if (!bothLines) return {};
60360 var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
60362 if (osmPathHighwayTagValues[entity1.tags.highway] || osmPathHighwayTagValues[entity2.tags.highway]) {
60363 // path-tram connections use this tag
60364 if (isTram) return {
60365 railway: 'tram_crossing'
60366 }; // other path-rail connections use this tag
60369 railway: 'crossing'
60372 // path-tram connections use this tag
60373 if (isTram) return {
60374 railway: 'tram_level_crossing'
60375 }; // other road-rail connections use this tag
60378 railway: 'level_crossing'
60383 if (featureTypes.indexOf('waterway') !== -1) {
60384 // do not allow fords on structures
60385 if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
60386 if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
60388 if (highwaysDisallowingFords[entity1.tags.highway] || highwaysDisallowingFords[entity2.tags.highway]) {
60389 // do not allow fords on major highways
60393 return bothLines ? {
60403 function findCrossingsByWay(way1, graph, tree) {
60404 var edgeCrossInfos = [];
60405 if (way1.type !== 'way') return edgeCrossInfos;
60406 var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
60407 var way1FeatureType = getFeatureType(taggedFeature1, graph);
60408 if (way1FeatureType === null) return edgeCrossInfos;
60409 var checkedSingleCrossingWays = {}; // declare vars ahead of time to reduce garbage collection
60413 var n1, n2, nA, nB, nAId, nBId;
60414 var segment1, segment2;
60416 var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
60417 var way1Nodes = graph.childNodes(way1);
60418 var comparedWays = {};
60420 for (i = 0; i < way1Nodes.length - 1; i++) {
60422 n2 = way1Nodes[i + 1];
60423 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
60424 // of overlapping ways
60426 segmentInfos = tree.waySegments(extent, graph);
60428 for (j = 0; j < segmentInfos.length; j++) {
60429 segment2Info = segmentInfos[j]; // don't check for self-intersection in this validation
60431 if (segment2Info.wayId === way1.id) continue; // skip if this way was already checked and only one issue is needed
60433 if (checkedSingleCrossingWays[segment2Info.wayId]) continue; // mark this way as checked even if there are no crossings
60435 comparedWays[segment2Info.wayId] = true;
60436 way2 = graph.hasEntity(segment2Info.wayId);
60437 if (!way2) continue;
60438 taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph); // only check crossing highway, waterway, building, and railway
60440 way2FeatureType = getFeatureType(taggedFeature2, graph);
60442 if (way2FeatureType === null || isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
60444 } // create only one issue for building crossings
60447 oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
60448 nAId = segment2Info.nodes[0];
60449 nBId = segment2Info.nodes[1];
60451 if (nAId === n1.id || nAId === n2.id || nBId === n1.id || nBId === n2.id) {
60452 // n1 or n2 is a connection node; skip
60456 nA = graph.hasEntity(nAId);
60458 nB = graph.hasEntity(nBId);
60460 segment1 = [n1.loc, n2.loc];
60461 segment2 = [nA.loc, nB.loc];
60462 var point = geoLineIntersection(segment1, segment2);
60465 edgeCrossInfos.push({
60468 featureType: way1FeatureType,
60469 edge: [n1.id, n2.id]
60472 featureType: way2FeatureType,
60473 edge: [nA.id, nB.id]
60479 checkedSingleCrossingWays[way2.id] = true;
60486 return edgeCrossInfos;
60489 function waysToCheck(entity, graph) {
60490 var featureType = getFeatureType(entity, graph);
60491 if (!featureType) return [];
60493 if (entity.type === 'way') {
60495 } else if (entity.type === 'relation') {
60496 return entity.members.reduce(function (array, member) {
60497 if (member.type === 'way' && ( // only look at geometry ways
60498 !member.role || member.role === 'outer' || member.role === 'inner')) {
60499 var entity = graph.hasEntity(member.id); // don't add duplicates
60501 if (entity && array.indexOf(entity) === -1) {
60502 array.push(entity);
60513 var validation = function checkCrossingWays(entity, graph) {
60514 var tree = context.history().tree();
60515 var ways = waysToCheck(entity, graph);
60516 var issues = []; // declare these here to reduce garbage collection
60518 var wayIndex, crossingIndex, crossings;
60520 for (wayIndex in ways) {
60521 crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
60523 for (crossingIndex in crossings) {
60524 issues.push(createIssue(crossings[crossingIndex], graph));
60531 function createIssue(crossing, graph) {
60532 // use the entities with the tags that define the feature type
60533 crossing.wayInfos.sort(function (way1Info, way2Info) {
60534 var type1 = way1Info.featureType;
60535 var type2 = way2Info.featureType;
60537 if (type1 === type2) {
60538 return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
60539 } else if (type1 === 'waterway') {
60541 } else if (type2 === 'waterway') {
60545 return type1 < type2;
60547 var entities = crossing.wayInfos.map(function (wayInfo) {
60548 return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
60550 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
60551 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
60552 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
60553 var featureType1 = crossing.wayInfos[0].featureType;
60554 var featureType2 = crossing.wayInfos[1].featureType;
60555 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
60556 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
60557 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
60558 var subtype = [featureType1, featureType2].sort().join('-');
60559 var crossingTypeID = subtype;
60561 if (isCrossingIndoors) {
60562 crossingTypeID = 'indoor-indoor';
60563 } else if (isCrossingTunnels) {
60564 crossingTypeID = 'tunnel-tunnel';
60565 } else if (isCrossingBridges) {
60566 crossingTypeID = 'bridge-bridge';
60569 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
60570 crossingTypeID += '_connectable';
60573 return new validationIssue({
60576 severity: 'warning',
60577 message: function message(context) {
60578 var graph = context.graph();
60579 var entity1 = graph.hasEntity(this.entityIds[0]),
60580 entity2 = graph.hasEntity(this.entityIds[1]);
60581 return entity1 && entity2 ? _t.html('issues.crossing_ways.message', {
60582 feature: utilDisplayLabel(entity1, graph),
60583 feature2: utilDisplayLabel(entity2, graph)
60586 reference: showReference,
60587 entityIds: entities.map(function (entity) {
60592 featureTypes: featureTypes,
60593 connectionTags: connectionTags
60595 // differentiate based on the loc since two ways can cross multiple times
60596 hash: crossing.crossPoint.toString() + // if the edges change then so does the fix
60597 edges.slice().sort(function (edge1, edge2) {
60598 // order to assure hash is deterministic
60599 return edge1[0] < edge2[0] ? -1 : 1;
60600 }).toString() + // ensure the correct connection tags are added in the fix
60601 JSON.stringify(connectionTags),
60602 loc: crossing.crossPoint,
60603 dynamicFixes: function dynamicFixes(context) {
60604 var mode = context.mode();
60605 if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
60606 var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
60607 var selectedFeatureType = this.data.featureTypes[selectedIndex];
60608 var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
60611 if (connectionTags) {
60612 fixes.push(makeConnectWaysFix(this.data.connectionTags));
60615 if (isCrossingIndoors) {
60616 fixes.push(new validationIssueFix({
60617 icon: 'iD-icon-layers',
60618 title: _t.html('issues.fix.use_different_levels.title')
60620 } else if (isCrossingTunnels || isCrossingBridges || featureType1 === 'building' || featureType2 === 'building') {
60621 fixes.push(makeChangeLayerFix('higher'));
60622 fixes.push(makeChangeLayerFix('lower')); // can only add bridge/tunnel if both features are lines
60623 } else if (context.graph().geometry(this.entityIds[0]) === 'line' && context.graph().geometry(this.entityIds[1]) === 'line') {
60624 // don't recommend adding bridges to waterways since they're uncommon
60625 if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
60626 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
60627 } // don't recommend adding tunnels under waterways since they're uncommon
60630 var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
60632 if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
60633 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
60635 } // repositioning the features is always an option
60638 fixes.push(new validationIssueFix({
60639 icon: 'iD-operation-move',
60640 title: _t.html('issues.fix.reposition_features.title')
60646 function showReference(selection) {
60647 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.crossing_ways.' + crossingTypeID + '.reference'));
60651 function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
60652 return new validationIssueFix({
60654 title: _t.html('issues.fix.' + fixTitleID + '.title'),
60655 onClick: function onClick(context) {
60656 var mode = context.mode();
60657 if (!mode || mode.id !== 'select') return;
60658 var selectedIDs = mode.selectedIDs();
60659 if (selectedIDs.length !== 1) return;
60660 var selectedWayID = selectedIDs[0];
60661 if (!context.hasEntity(selectedWayID)) return;
60662 var resultWayIDs = [selectedWayID];
60663 var edge, crossedEdge, crossedWayID;
60665 if (this.issue.entityIds[0] === selectedWayID) {
60666 edge = this.issue.data.edges[0];
60667 crossedEdge = this.issue.data.edges[1];
60668 crossedWayID = this.issue.entityIds[1];
60670 edge = this.issue.data.edges[1];
60671 crossedEdge = this.issue.data.edges[0];
60672 crossedWayID = this.issue.entityIds[0];
60675 var crossingLoc = this.issue.loc;
60676 var projection = context.projection;
60678 var action = function actionAddStructure(graph) {
60679 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
60680 var crossedWay = graph.hasEntity(crossedWayID); // use the explicit width of the crossed feature as the structure length, if available
60682 var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
60684 if (!structLengthMeters) {
60685 // if no explicit width is set, approximate the width based on the tags
60686 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
60689 if (structLengthMeters) {
60690 if (getFeatureType(crossedWay, graph) === 'railway') {
60691 // bridges over railways are generally much longer than the rail bed itself, compensate
60692 structLengthMeters *= 2;
60695 // should ideally never land here since all rail/water/road tags should have an implied width
60696 structLengthMeters = 8;
60699 var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
60700 var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
60701 var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
60702 if (crossingAngle > Math.PI) crossingAngle -= Math.PI; // lengthen the structure to account for the angle of the crossing
60704 structLengthMeters = structLengthMeters / 2 / Math.sin(crossingAngle) * 2; // add padding since the structure must extend past the edges of the crossed feature
60706 structLengthMeters += 4; // clamp the length to a reasonable range
60708 structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
60710 function geomToProj(geoPoint) {
60711 return [geoLonToMeters(geoPoint[0], geoPoint[1]), geoLatToMeters(geoPoint[1])];
60714 function projToGeom(projPoint) {
60715 var lat = geoMetersToLat(projPoint[1]);
60716 return [geoMetersToLon(projPoint[0], lat), lat];
60719 var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
60720 var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
60721 var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
60722 var projectedCrossingLoc = geomToProj(crossingLoc);
60723 var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
60725 function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
60726 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
60727 return projToGeom([projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters]);
60730 var endpointLocGetter1 = function endpointLocGetter1(lengthMeters) {
60731 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
60734 var endpointLocGetter2 = function endpointLocGetter2(lengthMeters) {
60735 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
60736 }; // avoid creating very short edges from splitting too close to another node
60739 var minEdgeLengthMeters = 0.55; // decide where to bound the structure along the way, splitting as necessary
60741 function determineEndpoint(edge, endNode, locGetter) {
60743 var idealLengthMeters = structLengthMeters / 2; // distance between the crossing location and the end of the edge,
60744 // the maximum length of this side of the structure
60746 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
60748 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
60749 // the edge is long enough to insert a new node
60750 // the loc that would result in the full expected length
60751 var idealNodeLoc = locGetter(idealLengthMeters);
60752 newNode = osmNode();
60753 graph = actionAddMidpoint({
60756 }, newNode)(graph);
60759 endNode.parentIntersectionWays(graph).forEach(function (way) {
60760 way.nodes.forEach(function (nodeID) {
60761 if (nodeID === endNode.id) {
60762 if (endNode.id === way.first() && endNode.id !== way.last() || endNode.id === way.last() && endNode.id !== way.first()) {
60771 if (edgeCount >= 3) {
60772 // the end node is a junction, try to leave a segment
60773 // between it and the structure - #7202
60774 var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
60776 if (insetLength > minEdgeLengthMeters) {
60777 var insetNodeLoc = locGetter(insetLength);
60778 newNode = osmNode();
60779 graph = actionAddMidpoint({
60782 }, newNode)(graph);
60785 } // if the edge is too short to subdivide as desired, then
60786 // just bound the structure at the existing end node
60789 if (!newNode) newNode = endNode;
60790 var splitAction = actionSplit([newNode.id]).limitWays(resultWayIDs); // only split selected or created ways
60793 graph = splitAction(graph);
60795 if (splitAction.getCreatedWayIDs().length) {
60796 resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
60802 var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
60803 var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
60804 var structureWay = resultWayIDs.map(function (id) {
60805 return graph.entity(id);
60806 }).find(function (way) {
60807 return way.nodes.indexOf(structEndNode1.id) !== -1 && way.nodes.indexOf(structEndNode2.id) !== -1;
60809 var tags = Object.assign({}, structureWay.tags); // copy tags
60811 if (bridgeOrTunnel === 'bridge') {
60812 tags.bridge = 'yes';
60815 var tunnelValue = 'yes';
60817 if (getFeatureType(structureWay, graph) === 'waterway') {
60818 // use `tunnel=culvert` for waterways by default
60819 tunnelValue = 'culvert';
60822 tags.tunnel = tunnelValue;
60824 } // apply the structure tags to the way
60827 graph = actionChangeTags(structureWay.id, tags)(graph);
60831 context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
60832 context.enter(modeSelect(context, resultWayIDs));
60837 function makeConnectWaysFix(connectionTags) {
60838 var fixTitleID = 'connect_features';
60840 if (connectionTags.ford) {
60841 fixTitleID = 'connect_using_ford';
60844 return new validationIssueFix({
60845 icon: 'iD-icon-crossing',
60846 title: _t.html('issues.fix.' + fixTitleID + '.title'),
60847 onClick: function onClick(context) {
60848 var loc = this.issue.loc;
60849 var connectionTags = this.issue.data.connectionTags;
60850 var edges = this.issue.data.edges;
60851 context.perform(function actionConnectCrossingWays(graph) {
60852 // create the new node for the points
60853 var node = osmNode({
60855 tags: connectionTags
60857 graph = graph.replace(node);
60858 var nodesToMerge = [node.id];
60859 var mergeThresholdInMeters = 0.75;
60860 edges.forEach(function (edge) {
60861 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
60862 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc); // if there is already a point nearby, use that
60864 if (closestNodeInfo.distance < mergeThresholdInMeters) {
60865 nodesToMerge.push(closestNodeInfo.node.id); // else add the new node to the way
60867 graph = actionAddMidpoint({
60874 if (nodesToMerge.length > 1) {
60875 // if we're using nearby nodes, merge them with the new node
60876 graph = actionMergeNodes(nodesToMerge, loc)(graph);
60880 }, _t('issues.fix.connect_crossing_features.annotation'));
60885 function makeChangeLayerFix(higherOrLower) {
60886 return new validationIssueFix({
60887 icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
60888 title: _t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'),
60889 onClick: function onClick(context) {
60890 var mode = context.mode();
60891 if (!mode || mode.id !== 'select') return;
60892 var selectedIDs = mode.selectedIDs();
60893 if (selectedIDs.length !== 1) return;
60894 var selectedID = selectedIDs[0];
60895 if (!this.issue.entityIds.some(function (entityId) {
60896 return entityId === selectedID;
60898 var entity = context.hasEntity(selectedID);
60899 if (!entity) return;
60900 var tags = Object.assign({}, entity.tags); // shallow copy
60902 var layer = tags.layer && Number(tags.layer);
60904 if (layer && !isNaN(layer)) {
60905 if (higherOrLower === 'higher') {
60911 if (higherOrLower === 'higher') {
60918 tags.layer = layer.toString();
60919 context.perform(actionChangeTags(entity.id, tags), _t('operations.change_tags.annotation'));
60924 validation.type = type;
60928 function validationDisconnectedWay() {
60929 var type = 'disconnected_way';
60931 function isTaggedAsHighway(entity) {
60932 return osmRoutableHighwayTagValues[entity.tags.highway];
60935 var validation = function checkDisconnectedWay(entity, graph) {
60936 var routingIslandWays = routingIslandForEntity(entity);
60937 if (!routingIslandWays) return [];
60938 return [new validationIssue({
60940 subtype: 'highway',
60941 severity: 'warning',
60942 message: function message(context) {
60943 var entity = this.entityIds.length && context.hasEntity(this.entityIds[0]);
60944 var label = entity && utilDisplayLabel(entity, context.graph());
60945 return _t.html('issues.disconnected_way.routable.message', {
60946 count: this.entityIds.length,
60950 reference: showReference,
60951 entityIds: Array.from(routingIslandWays).map(function (way) {
60954 dynamicFixes: makeFixes
60957 function makeFixes(context) {
60959 var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
60961 if (singleEntity) {
60962 if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
60963 var textDirection = _mainLocalizer.textDirection();
60964 var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
60965 if (startFix) fixes.push(startFix);
60966 var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
60967 if (endFix) fixes.push(endFix);
60970 if (!fixes.length) {
60971 fixes.push(new validationIssueFix({
60972 title: _t.html('issues.fix.connect_feature.title')
60976 fixes.push(new validationIssueFix({
60977 icon: 'iD-operation-delete',
60978 title: _t.html('issues.fix.delete_feature.title'),
60979 entityIds: [singleEntity.id],
60980 onClick: function onClick(context) {
60981 var id = this.issue.entityIds[0];
60982 var operation = operationDelete(context, [id]);
60984 if (!operation.disabled()) {
60990 fixes.push(new validationIssueFix({
60991 title: _t.html('issues.fix.connect_features.title')
60998 function showReference(selection) {
60999 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.disconnected_way.routable.reference'));
61002 function routingIslandForEntity(entity) {
61003 var routingIsland = new Set(); // the interconnected routable features
61005 var waysToCheck = []; // the queue of remaining routable ways to traverse
61007 function queueParentWays(node) {
61008 graph.parentWays(node).forEach(function (parentWay) {
61009 if (!routingIsland.has(parentWay) && // only check each feature once
61010 isRoutableWay(parentWay, false)) {
61011 // only check routable features
61012 routingIsland.add(parentWay);
61013 waysToCheck.push(parentWay);
61018 if (entity.type === 'way' && isRoutableWay(entity, true)) {
61019 routingIsland.add(entity);
61020 waysToCheck.push(entity);
61021 } else if (entity.type === 'node' && isRoutableNode(entity)) {
61022 routingIsland.add(entity);
61023 queueParentWays(entity);
61025 // this feature isn't routable, cannot be a routing island
61029 while (waysToCheck.length) {
61030 var wayToCheck = waysToCheck.pop();
61031 var childNodes = graph.childNodes(wayToCheck);
61033 for (var i in childNodes) {
61034 var vertex = childNodes[i];
61036 if (isConnectedVertex(vertex)) {
61037 // found a link to the wider network, not a routing island
61041 if (isRoutableNode(vertex)) {
61042 routingIsland.add(vertex);
61045 queueParentWays(vertex);
61047 } // no network link found, this is a routing island, return its members
61050 return routingIsland;
61053 function isConnectedVertex(vertex) {
61054 // assume ways overlapping unloaded tiles are connected to the wider road network - #5938
61055 var osm = services.osm;
61056 if (osm && !osm.isDataLoaded(vertex.loc)) return true; // entrances are considered connected
61058 if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return true;
61059 if (vertex.tags.amenity === 'parking_entrance') return true;
61063 function isRoutableNode(node) {
61064 // treat elevators as distinct features in the highway network
61065 if (node.tags.highway === 'elevator') return true;
61069 function isRoutableWay(way, ignoreInnerWays) {
61070 if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
61071 return graph.parentRelations(way).some(function (parentRelation) {
61072 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true;
61073 if (parentRelation.isMultipolygon() && isTaggedAsHighway(parentRelation) && (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
61078 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
61079 var vertex = graph.hasEntity(vertexID);
61080 if (!vertex || vertex.tags.noexit === 'yes') return null;
61081 var useLeftContinue = whichEnd === 'start' && textDirection === 'ltr' || whichEnd === 'end' && textDirection === 'rtl';
61082 return new validationIssueFix({
61083 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
61084 title: _t.html('issues.fix.continue_from_' + whichEnd + '.title'),
61085 entityIds: [vertexID],
61086 onClick: function onClick(context) {
61087 var wayId = this.issue.entityIds[0];
61088 var way = context.hasEntity(wayId);
61089 var vertexId = this.entityIds[0];
61090 var vertex = context.hasEntity(vertexId);
61091 if (!way || !vertex) return; // make sure the vertex is actually visible and editable
61093 var map = context.map();
61095 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
61096 map.zoomToEase(vertex);
61099 context.enter(modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true));
61105 validation.type = type;
61109 function validationFormatting() {
61110 var type = 'invalid_format';
61112 var validation = function validation(entity) {
61115 function isValidEmail(email) {
61116 // Emails in OSM are going to be official so they should be pretty simple
61117 // Using negated lists to better support all possible unicode characters (#6494)
61118 var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; // An empty value is also acceptable
61120 return !email || valid_email.test(email);
61123 function isSchemePresent(url) {
61124 var valid_scheme = /^https?:\/\//i;
61125 return (!url || valid_scheme.test(url));
61130 function showReferenceEmail(selection) {
61131 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.invalid_format.email.reference'));
61134 function showReferenceWebsite(selection) {
61135 selection.selectAll('.issue-reference')
61139 .attr('class', 'issue-reference')
61140 .html(t.html('issues.invalid_format.website.reference'));
61142 if (entity.tags.website) {
61143 // Multiple websites are possible
61144 // If ever we support ES6, arrow functions make this nicer
61145 var websites = entity.tags.website
61147 .map(function(s) { return s.trim(); })
61148 .filter(function(x) { return !isSchemePresent(x); });
61149 if (websites.length) {
61150 issues.push(new validationIssue({
61152 subtype: 'website',
61153 severity: 'warning',
61154 message: function(context) {
61155 var entity = context.hasEntity(this.entityIds[0]);
61156 return entity ? t.html('issues.invalid_format.website.message' + this.data,
61157 { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
61159 reference: showReferenceWebsite,
61160 entityIds: [entity.id],
61161 hash: websites.join(),
61162 data: (websites.length > 1) ? '_multi' : ''
61169 if (entity.tags.email) {
61170 // Multiple emails are possible
61171 var emails = entity.tags.email.split(';').map(function (s) {
61173 }).filter(function (x) {
61174 return !isValidEmail(x);
61177 if (emails.length) {
61178 issues.push(new validationIssue({
61181 severity: 'warning',
61182 message: function message(context) {
61183 var entity = context.hasEntity(this.entityIds[0]);
61184 return entity ? _t.html('issues.invalid_format.email.message' + this.data, {
61185 feature: utilDisplayLabel(entity, context.graph()),
61186 email: emails.join(', ')
61189 reference: showReferenceEmail,
61190 entityIds: [entity.id],
61191 hash: emails.join(),
61192 data: emails.length > 1 ? '_multi' : ''
61200 validation.type = type;
61204 function validationHelpRequest(context) {
61205 var type = 'help_request';
61207 var validation = function checkFixmeTag(entity) {
61208 if (!entity.tags.fixme) return []; // don't flag fixmes on features added by the user
61210 if (entity.version === undefined) return [];
61212 if (entity.v !== undefined) {
61213 var baseEntity = context.history().base().hasEntity(entity.id); // don't flag fixmes added by the user on existing features
61215 if (!baseEntity || !baseEntity.tags.fixme) return [];
61218 return [new validationIssue({
61220 subtype: 'fixme_tag',
61221 severity: 'warning',
61222 message: function message(context) {
61223 var entity = context.hasEntity(this.entityIds[0]);
61224 return entity ? _t.html('issues.fixme_tag.message', {
61225 feature: utilDisplayLabel(entity, context.graph())
61228 dynamicFixes: function dynamicFixes() {
61229 return [new validationIssueFix({
61230 title: _t.html('issues.fix.address_the_concern.title')
61233 reference: showReference,
61234 entityIds: [entity.id]
61237 function showReference(selection) {
61238 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.fixme_tag.reference'));
61242 validation.type = type;
61246 function validationImpossibleOneway() {
61247 var type = 'impossible_oneway';
61249 var validation = function checkImpossibleOneway(entity, graph) {
61250 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
61251 if (entity.isClosed()) return [];
61252 if (!typeForWay(entity)) return [];
61253 if (!isOneway(entity)) return [];
61254 var firstIssues = issuesForNode(entity, entity.first());
61255 var lastIssues = issuesForNode(entity, entity.last());
61256 return firstIssues.concat(lastIssues);
61258 function typeForWay(way) {
61259 if (way.geometry(graph) !== 'line') return null;
61260 if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
61261 if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
61265 function isOneway(way) {
61266 if (way.tags.oneway === 'yes') return true;
61267 if (way.tags.oneway) return false;
61269 for (var key in way.tags) {
61270 if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
61278 function nodeOccursMoreThanOnce(way, nodeID) {
61279 var occurrences = 0;
61281 for (var index in way.nodes) {
61282 if (way.nodes[index] === nodeID) {
61284 if (occurrences > 1) return true;
61291 function isConnectedViaOtherTypes(way, node) {
61292 var wayType = typeForWay(way);
61294 if (wayType === 'highway') {
61295 // entrances are considered connected
61296 if (node.tags.entrance && node.tags.entrance !== 'no') return true;
61297 if (node.tags.amenity === 'parking_entrance') return true;
61298 } else if (wayType === 'waterway') {
61299 if (node.id === way.first()) {
61300 // multiple waterways may start at the same spring
61301 if (node.tags.natural === 'spring') return true;
61303 // multiple waterways may end at the same drain
61304 if (node.tags.manhole === 'drain') return true;
61308 return graph.parentWays(node).some(function (parentWay) {
61309 if (parentWay.id === way.id) return false;
61311 if (wayType === 'highway') {
61312 // allow connections to highway areas
61313 if (parentWay.geometry(graph) === 'area' && osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; // count connections to ferry routes as connected
61315 if (parentWay.tags.route === 'ferry') return true;
61316 return graph.parentRelations(parentWay).some(function (parentRelation) {
61317 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; // allow connections to highway multipolygons
61319 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
61321 } else if (wayType === 'waterway') {
61322 // multiple waterways may start or end at a water body at the same node
61323 if (parentWay.tags.natural === 'water' || parentWay.tags.natural === 'coastline') return true;
61330 function issuesForNode(way, nodeID) {
61331 var isFirst = nodeID === way.first();
61332 var wayType = typeForWay(way); // ignore if this way is self-connected at this node
61334 if (nodeOccursMoreThanOnce(way, nodeID)) return [];
61335 var osm = services.osm;
61336 if (!osm) return [];
61337 var node = graph.hasEntity(nodeID); // ignore if this node or its tile are unloaded
61339 if (!node || !osm.isDataLoaded(node.loc)) return [];
61340 if (isConnectedViaOtherTypes(way, node)) return [];
61341 var attachedWaysOfSameType = graph.parentWays(node).filter(function (parentWay) {
61342 if (parentWay.id === way.id) return false;
61343 return typeForWay(parentWay) === wayType;
61344 }); // assume it's okay for waterways to start or end disconnected for now
61346 if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
61347 var attachedOneways = attachedWaysOfSameType.filter(function (attachedWay) {
61348 return isOneway(attachedWay);
61349 }); // ignore if the way is connected to some non-oneway features
61351 if (attachedOneways.length < attachedWaysOfSameType.length) return [];
61353 if (attachedOneways.length) {
61354 var connectedEndpointsOkay = attachedOneways.some(function (attachedOneway) {
61355 if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
61356 if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
61359 if (connectedEndpointsOkay) return [];
61362 var placement = isFirst ? 'start' : 'end',
61363 messageID = wayType + '.',
61364 referenceID = wayType + '.';
61366 if (wayType === 'waterway') {
61367 messageID += 'connected.' + placement;
61368 referenceID += 'connected';
61370 messageID += placement;
61371 referenceID += placement;
61374 return [new validationIssue({
61377 severity: 'warning',
61378 message: function message(context) {
61379 var entity = context.hasEntity(this.entityIds[0]);
61380 return entity ? _t.html('issues.impossible_oneway.' + messageID + '.message', {
61381 feature: utilDisplayLabel(entity, context.graph())
61384 reference: getReference(referenceID),
61385 entityIds: [way.id, node.id],
61386 dynamicFixes: function dynamicFixes() {
61389 if (attachedOneways.length) {
61390 fixes.push(new validationIssueFix({
61391 icon: 'iD-operation-reverse',
61392 title: _t.html('issues.fix.reverse_feature.title'),
61393 entityIds: [way.id],
61394 onClick: function onClick(context) {
61395 var id = this.issue.entityIds[0];
61396 context.perform(actionReverse(id), _t('operations.reverse.annotation.line', {
61403 if (node.tags.noexit !== 'yes') {
61404 var textDirection = _mainLocalizer.textDirection();
61405 var useLeftContinue = isFirst && textDirection === 'ltr' || !isFirst && textDirection === 'rtl';
61406 fixes.push(new validationIssueFix({
61407 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
61408 title: _t.html('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
61409 onClick: function onClick(context) {
61410 var entityID = this.issue.entityIds[0];
61411 var vertexID = this.issue.entityIds[1];
61412 var way = context.entity(entityID);
61413 var vertex = context.entity(vertexID);
61414 continueDrawing(way, vertex, context);
61424 function getReference(referenceID) {
61425 return function showReference(selection) {
61426 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.impossible_oneway.' + referenceID + '.reference'));
61432 function continueDrawing(way, vertex, context) {
61433 // make sure the vertex is actually visible and editable
61434 var map = context.map();
61436 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
61437 map.zoomToEase(vertex);
61440 context.enter(modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true));
61443 validation.type = type;
61447 function validationIncompatibleSource() {
61448 var type = 'incompatible_source';
61449 var invalidSources = [{
61452 exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
61455 var validation = function checkIncompatibleSource(entity) {
61456 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
61457 if (!entitySources) return [];
61459 invalidSources.forEach(function (invalidSource) {
61460 var hasInvalidSource = entitySources.some(function (source) {
61461 if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
61462 if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
61465 if (!hasInvalidSource) return;
61466 issues.push(new validationIssue({
61468 severity: 'warning',
61469 message: function message(context) {
61470 var entity = context.hasEntity(this.entityIds[0]);
61471 return entity ? _t.html('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
61472 feature: utilDisplayLabel(entity, context.graph())
61475 reference: getReference(invalidSource.id),
61476 entityIds: [entity.id],
61477 dynamicFixes: function dynamicFixes() {
61478 return [new validationIssueFix({
61479 title: _t.html('issues.fix.remove_proprietary_data.title')
61486 function getReference(id) {
61487 return function showReference(selection) {
61488 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.incompatible_source.' + id + '.reference'));
61493 validation.type = type;
61497 function validationMaprules() {
61498 var type = 'maprules';
61500 var validation = function checkMaprules(entity, graph) {
61501 if (!services.maprules) return [];
61502 var rules = services.maprules.validationRules();
61505 for (var i = 0; i < rules.length; i++) {
61506 var rule = rules[i];
61507 rule.findIssues(entity, graph, issues);
61513 validation.type = type;
61517 function validationMismatchedGeometry() {
61518 var type = 'mismatched_geometry';
61520 function tagSuggestingLineIsArea(entity) {
61521 if (entity.type !== 'way' || entity.isClosed()) return null;
61522 var tagSuggestingArea = entity.tagSuggestingArea();
61524 if (!tagSuggestingArea) {
61528 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
61529 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
61531 if (asLine && asArea && asLine === asArea) {
61532 // these tags also allow lines and making this an area wouldn't matter
61536 return tagSuggestingArea;
61539 function makeConnectEndpointsFixOnClick(way, graph) {
61540 // must have at least three nodes to close this automatically
61541 if (way.nodes.length < 3) return null;
61542 var nodes = graph.childNodes(way),
61544 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length - 1].loc); // if the distance is very small, attempt to merge the endpoints
61546 if (firstToLastDistanceMeters < 0.75) {
61547 testNodes = nodes.slice(); // shallow copy
61550 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
61552 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
61553 return function (context) {
61554 var way = context.entity(this.issue.entityIds[0]);
61555 context.perform(actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length - 1]], nodes[0].loc), _t('issues.fix.connect_endpoints.annotation'));
61558 } // if the points were not merged, attempt to close the way
61561 testNodes = nodes.slice(); // shallow copy
61563 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
61565 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
61566 return function (context) {
61567 var wayId = this.issue.entityIds[0];
61568 var way = context.entity(wayId);
61569 var nodeId = way.nodes[0];
61570 var index = way.nodes.length;
61571 context.perform(actionAddVertex(wayId, nodeId, index), _t('issues.fix.connect_endpoints.annotation'));
61576 function lineTaggedAsAreaIssue(entity) {
61577 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
61578 if (!tagSuggestingArea) return null;
61579 return new validationIssue({
61581 subtype: 'area_as_line',
61582 severity: 'warning',
61583 message: function message(context) {
61584 var entity = context.hasEntity(this.entityIds[0]);
61585 return entity ? _t.html('issues.tag_suggests_area.message', {
61586 feature: utilDisplayLabel(entity, 'area'),
61588 tags: tagSuggestingArea
61592 reference: showReference,
61593 entityIds: [entity.id],
61594 hash: JSON.stringify(tagSuggestingArea),
61595 dynamicFixes: function dynamicFixes(context) {
61597 var entity = context.entity(this.entityIds[0]);
61598 var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
61599 fixes.push(new validationIssueFix({
61600 title: _t.html('issues.fix.connect_endpoints.title'),
61601 onClick: connectEndsOnClick
61603 fixes.push(new validationIssueFix({
61604 icon: 'iD-operation-delete',
61605 title: _t.html('issues.fix.remove_tag.title'),
61606 onClick: function onClick(context) {
61607 var entityId = this.issue.entityIds[0];
61608 var entity = context.entity(entityId);
61609 var tags = Object.assign({}, entity.tags); // shallow copy
61611 for (var key in tagSuggestingArea) {
61615 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_tag.annotation'));
61622 function showReference(selection) {
61623 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.tag_suggests_area.reference'));
61627 function vertexTaggedAsPointIssue(entity, graph) {
61628 // we only care about nodes
61629 if (entity.type !== 'node') return null; // ignore tagless points
61631 if (Object.keys(entity.tags).length === 0) return null; // address lines are special so just ignore them
61633 if (entity.isOnAddressLine(graph)) return null;
61634 var geometry = entity.geometry(graph);
61635 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
61637 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
61638 return new validationIssue({
61640 subtype: 'vertex_as_point',
61641 severity: 'warning',
61642 message: function message(context) {
61643 var entity = context.hasEntity(this.entityIds[0]);
61644 return entity ? _t.html('issues.vertex_as_point.message', {
61645 feature: utilDisplayLabel(entity, 'vertex')
61648 reference: function showReference(selection) {
61649 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.vertex_as_point.reference'));
61651 entityIds: [entity.id]
61653 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
61654 return new validationIssue({
61656 subtype: 'point_as_vertex',
61657 severity: 'warning',
61658 message: function message(context) {
61659 var entity = context.hasEntity(this.entityIds[0]);
61660 return entity ? _t.html('issues.point_as_vertex.message', {
61661 feature: utilDisplayLabel(entity, 'point')
61664 reference: function showReference(selection) {
61665 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.point_as_vertex.reference'));
61667 entityIds: [entity.id],
61668 dynamicFixes: function dynamicFixes(context) {
61669 var entityId = this.entityIds[0];
61670 var extractOnClick = null;
61672 if (!context.hasHiddenConnections(entityId)) {
61673 extractOnClick = function extractOnClick(context) {
61674 var entityId = this.issue.entityIds[0];
61675 var action = actionExtract(entityId);
61676 context.perform(action, _t('operations.extract.annotation', {
61678 })); // re-enter mode to trigger updates
61680 context.enter(modeSelect(context, [action.getExtractedNodeID()]));
61684 return [new validationIssueFix({
61685 icon: 'iD-operation-extract',
61686 title: _t.html('issues.fix.extract_point.title'),
61687 onClick: extractOnClick
61696 function unclosedMultipolygonPartIssues(entity, graph) {
61697 if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations
61698 !entity.isComplete(graph)) return null;
61699 var sequences = osmJoinWays(entity.members, graph);
61702 for (var i in sequences) {
61703 var sequence = sequences[i];
61704 if (!sequence.nodes) continue;
61705 var firstNode = sequence.nodes[0];
61706 var lastNode = sequence.nodes[sequence.nodes.length - 1]; // part is closed if the first and last nodes are the same
61708 if (firstNode === lastNode) continue;
61709 var issue = new validationIssue({
61711 subtype: 'unclosed_multipolygon_part',
61712 severity: 'warning',
61713 message: function message(context) {
61714 var entity = context.hasEntity(this.entityIds[0]);
61715 return entity ? _t.html('issues.unclosed_multipolygon_part.message', {
61716 feature: utilDisplayLabel(entity, context.graph())
61719 reference: showReference,
61720 loc: sequence.nodes[0].loc,
61721 entityIds: [entity.id],
61722 hash: sequence.map(function (way) {
61726 issues.push(issue);
61731 function showReference(selection) {
61732 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unclosed_multipolygon_part.reference'));
61736 var validation = function checkMismatchedGeometry(entity, graph) {
61737 var issues = [vertexTaggedAsPointIssue(entity, graph), lineTaggedAsAreaIssue(entity)];
61738 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
61739 return issues.filter(Boolean);
61742 validation.type = type;
61746 function validationMissingRole() {
61747 var type = 'missing_role';
61749 var validation = function checkMissingRole(entity, graph) {
61752 if (entity.type === 'way') {
61753 graph.parentRelations(entity).forEach(function (relation) {
61754 if (!relation.isMultipolygon()) return;
61755 var member = relation.memberById(entity.id);
61757 if (member && isMissingRole(member)) {
61758 issues.push(makeIssue(entity, relation, member));
61761 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
61762 entity.indexedMembers().forEach(function (member) {
61763 var way = graph.hasEntity(member.id);
61765 if (way && isMissingRole(member)) {
61766 issues.push(makeIssue(way, entity, member));
61774 function isMissingRole(member) {
61775 return !member.role || !member.role.trim().length;
61778 function makeIssue(way, relation, member) {
61779 return new validationIssue({
61781 severity: 'warning',
61782 message: function message(context) {
61783 var member = context.hasEntity(this.entityIds[1]),
61784 relation = context.hasEntity(this.entityIds[0]);
61785 return member && relation ? _t.html('issues.missing_role.message', {
61786 member: utilDisplayLabel(member, context.graph()),
61787 relation: utilDisplayLabel(relation, context.graph())
61790 reference: showReference,
61791 entityIds: [relation.id, way.id],
61795 hash: member.index.toString(),
61796 dynamicFixes: function dynamicFixes() {
61797 return [makeAddRoleFix('inner'), makeAddRoleFix('outer'), new validationIssueFix({
61798 icon: 'iD-operation-delete',
61799 title: _t.html('issues.fix.remove_from_relation.title'),
61800 onClick: function onClick(context) {
61801 context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t('operations.delete_member.annotation', {
61809 function showReference(selection) {
61810 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.missing_role.multipolygon.reference'));
61814 function makeAddRoleFix(role) {
61815 return new validationIssueFix({
61816 title: _t.html('issues.fix.set_as_' + role + '.title'),
61817 onClick: function onClick(context) {
61818 var oldMember = this.issue.data.member;
61820 id: this.issue.entityIds[1],
61821 type: oldMember.type,
61824 context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t('operations.change_role.annotation', {
61831 validation.type = type;
61835 function validationMissingTag(context) {
61836 var type = 'missing_tag';
61838 function hasDescriptiveTags(entity, graph) {
61839 var keys = Object.keys(entity.tags).filter(function (k) {
61840 if (k === 'area' || k === 'name') {
61843 return osmIsInterestingTag(k);
61847 if (entity.type === 'relation' && keys.length === 1 && entity.tags.type === 'multipolygon') {
61848 // this relation's only interesting tag just says its a multipolygon,
61849 // which is not descriptive enough
61850 // It's okay for a simple multipolygon to have no descriptive tags
61851 // if its outer way has them (old model, see `outdated_tags.js`)
61852 return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
61855 return keys.length > 0;
61858 function isUnknownRoad(entity) {
61859 return entity.type === 'way' && entity.tags.highway === 'road';
61862 function isUntypedRelation(entity) {
61863 return entity.type === 'relation' && !entity.tags.type;
61866 var validation = function checkMissingTag(entity, graph) {
61868 var osm = context.connection();
61869 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
61871 if (!isUnloadedNode && // allow untagged nodes that are part of ways
61872 entity.geometry(graph) !== 'vertex' && // allow untagged entities that are part of relations
61873 !entity.hasParentRelations(graph)) {
61874 if (Object.keys(entity.tags).length === 0) {
61876 } else if (!hasDescriptiveTags(entity, graph)) {
61877 subtype = 'descriptive';
61878 } else if (isUntypedRelation(entity)) {
61879 subtype = 'relation_type';
61881 } // flag an unknown road even if it's a member of a relation
61884 if (!subtype && isUnknownRoad(entity)) {
61885 subtype = 'highway_classification';
61888 if (!subtype) return [];
61889 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
61890 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; // can always delete if the user created it in the first place..
61892 var canDelete = entity.version === undefined || entity.v !== undefined;
61893 var severity = canDelete && subtype !== 'highway_classification' ? 'error' : 'warning';
61894 return [new validationIssue({
61897 severity: severity,
61898 message: function message(context) {
61899 var entity = context.hasEntity(this.entityIds[0]);
61900 return entity ? _t.html('issues.' + messageID + '.message', {
61901 feature: utilDisplayLabel(entity, context.graph())
61904 reference: showReference,
61905 entityIds: [entity.id],
61906 dynamicFixes: function dynamicFixes(context) {
61908 var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
61909 fixes.push(new validationIssueFix({
61910 icon: 'iD-icon-search',
61911 title: _t.html('issues.fix.' + selectFixType + '.title'),
61912 onClick: function onClick(context) {
61913 context.ui().sidebar.showPresetList();
61917 var id = this.entityIds[0];
61918 var operation = operationDelete(context, [id]);
61919 var disabledReasonID = operation.disabled();
61921 if (!disabledReasonID) {
61922 deleteOnClick = function deleteOnClick(context) {
61923 var id = this.issue.entityIds[0];
61924 var operation = operationDelete(context, [id]);
61926 if (!operation.disabled()) {
61932 fixes.push(new validationIssueFix({
61933 icon: 'iD-operation-delete',
61934 title: _t.html('issues.fix.delete_feature.title'),
61935 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
61936 onClick: deleteOnClick
61942 function showReference(selection) {
61943 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.' + referenceID + '.reference'));
61947 validation.type = type;
61951 var simplify = function simplify(str) {
61952 return diacritics.remove(str.replace(/&/g, 'and').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\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\uff01-\uff03\uff05-\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65]+/g, '').toLowerCase());
61956 // kvnd: "amenity/fast_food|Thaï Express~(North America)",
61957 // kvn: "amenity/fast_food|Thaï Express",
61958 // kv: "amenity/fast_food",
61961 // n: "Thaï Express",
61962 // d: "(North America)",
61963 // nsimple: "thaiexpress",
61964 // kvnnsimple: "amenity/fast_food|thaiexpress"
61967 var to_parts = function to_parts(kvnd) {
61970 var kvndparts = kvnd.split('~', 2);
61971 if (kvndparts.length > 1) parts.d = kvndparts[1];
61972 parts.kvn = kvndparts[0];
61973 var kvnparts = parts.kvn.split('|', 2);
61974 if (kvnparts.length > 1) parts.n = kvnparts[1];
61975 parts.kv = kvnparts[0];
61976 var kvparts = parts.kv.split('/', 2);
61977 parts.k = kvparts[0];
61978 parts.v = kvparts[1];
61979 parts.nsimple = simplify(parts.n);
61980 parts.kvnsimple = parts.kv + '|' + parts.nsimple;
61984 var matchGroups = {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/beverages"],camping:["leisure/park","tourism/camp_site","tourism/caravan_site"],car_parts:["shop/car_parts","shop/car_repair","shop/tires","shop/tyres"],confectionery:["shop/candy","shop/chocolate","shop/confectionery"],convenience:["shop/beauty","shop/chemist","shop/convenience","shop/cosmetics","shop/newsagent"],coworking:["amenity/coworking_space","office/coworking","office/coworking_space"],electronics:["office/telecommunication","shop/computer","shop/electronics","shop/hifi","shop/mobile","shop/mobile_phone","shop/telecommunication"],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/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/carpet","shop/diy","shop/doityourself","shop/doors","shop/electrical","shop/flooring","shop/hardware","shop/power_tools","shop/tool_hire","shop/tools","shop/trade"],health_food:["shop/health","shop/health_food","shop/herbalist","shop/nutrition_supplements"],houseware:["shop/houseware","shop/interior_decoration"],lodging:["tourism/hotel","tourism/motel"],money_transfer:["amenity/money_transfer","shop/money_transfer"],outdoor:["shop/outdoor","shop/sports"],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/supermarket","shop/discount","shop/convenience"],vending:["amenity/vending_machine","shop/vending_machine"],wholesale:["shop/wholesale","shop/supermarket","shop/department_store"]};
61986 matchGroups: matchGroups
61989 var matchGroups$1 = require$$0.matchGroups;
61991 var matcher$1 = function matcher() {
61992 var _warnings = []; // array of match conflict pairs
61994 var _ambiguous = {};
61995 var _matchIndex = {};
61996 var matcher = {}; // Create an index of all the keys/simplenames for fast matching
61998 matcher.buildMatchIndex = function (brands) {
61999 // two passes - once for primary names, once for secondary/alternate names
62000 Object.keys(brands).forEach(function (kvnd) {
62001 return insertNames(kvnd, 'primary');
62003 Object.keys(brands).forEach(function (kvnd) {
62004 return insertNames(kvnd, 'secondary');
62007 function insertNames(kvnd, which) {
62008 var obj = brands[kvnd];
62009 var parts = to_parts(kvnd); // Exit early for ambiguous names in the second pass.
62010 // They were collected in the first pass and we don't gather alt names for them.
62012 if (which === 'secondary' && parts.d) return;
62014 if (obj.countryCodes) {
62015 parts.countryCodes = obj.countryCodes.slice(); // copy
62018 var nomatches = obj.nomatch || [];
62020 if (nomatches.some(function (s) {
62023 console.log("WARNING match/nomatch conflict for ".concat(kvnd));
62027 var match_kv = [parts.kv].concat(obj.matchTags || []).concat(["".concat(parts.k, "/yes"), "building/yes"]) // #3454 - match some generic tags
62028 .map(function (s) {
62029 return s.toLowerCase();
62031 var match_nsimple = [];
62033 if (which === 'primary') {
62034 match_nsimple = [parts.n].concat(obj.matchNames || []).concat(obj.tags.official_name || []) // #2732 - match alternate names
62036 } else if (which === 'secondary') {
62037 match_nsimple = [].concat(obj.tags.alt_name || []) // #2732 - match alternate names
62038 .concat(obj.tags.short_name || []) // #2732 - match alternate names
62042 if (!match_nsimple.length) return; // nothing to do
62044 match_kv.forEach(function (kv) {
62045 match_nsimple.forEach(function (nsimple) {
62047 // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
62048 // FIXME: Name collisions will overwrite the initial entry (ok for now)
62049 if (!_ambiguous[kv]) _ambiguous[kv] = {};
62050 _ambiguous[kv][nsimple] = parts;
62052 // Names we mostly expect to be unique..
62053 if (!_matchIndex[kv]) _matchIndex[kv] = {};
62054 var m = _matchIndex[kv][nsimple];
62057 // There already is a match for this name, skip it
62058 // Warn if we detect collisions in a primary name.
62059 // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
62060 if (which === 'primary' && !/\/yes$/.test(kv)) {
62061 _warnings.push([m.kvnd, "".concat(kvnd, " (").concat(kv, "/").concat(nsimple, ")")]);
62064 _matchIndex[kv][nsimple] = parts; // insert
62070 }; // pass a `key`, `value`, `name` and return the best match,
62071 // `countryCode` optional (if supplied, must match that too)
62074 matcher.matchKVN = function (key, value, name, countryCode) {
62075 return matcher.matchParts(to_parts("".concat(key, "/").concat(value, "|").concat(name)), countryCode);
62076 }; // pass a parts object and return the best match,
62077 // `countryCode` optional (if supplied, must match that too)
62080 matcher.matchParts = function (parts, countryCode) {
62082 var inGroup = false; // fixme: we currently return a single match for ambiguous
62084 match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
62085 if (match && matchesCountryCode(match)) return match; // try to return an exact match
62087 match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
62088 if (match && matchesCountryCode(match)) return match; // look in match groups
62090 for (var mg in matchGroups$1) {
62091 var matchGroup = matchGroups$1[mg];
62095 for (var i = 0; i < matchGroup.length; i++) {
62096 var otherkv = matchGroup[i].toLowerCase();
62099 inGroup = otherkv === parts.kv;
62103 // fixme: we currently return a single match for ambiguous
62104 match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
62108 match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
62111 if (match && !matchesCountryCode(match)) {
62115 if (inGroup && match) {
62123 function matchesCountryCode(match) {
62124 if (!countryCode) return true;
62125 if (!match.countryCodes) return true;
62126 return match.countryCodes.indexOf(countryCode) !== -1;
62130 matcher.getWarnings = function () {
62137 var fromCharCode = String.fromCharCode;
62138 var nativeFromCodePoint = String.fromCodePoint;
62140 // length should be 1, old FF problem
62141 var INCORRECT_LENGTH = !!nativeFromCodePoint && nativeFromCodePoint.length != 1;
62143 // `String.fromCodePoint` method
62144 // https://tc39.github.io/ecma262/#sec-string.fromcodepoint
62145 _export({ target: 'String', stat: true, forced: INCORRECT_LENGTH }, {
62146 fromCodePoint: function fromCodePoint(x) { // eslint-disable-line no-unused-vars
62148 var length = arguments.length;
62151 while (length > i) {
62152 code = +arguments[i++];
62153 if (toAbsoluteIndex(code, 0x10FFFF) !== code) throw RangeError(code + ' is not a valid code point');
62154 elements.push(code < 0x10000
62155 ? fromCharCode(code)
62156 : fromCharCode(((code -= 0x10000) >> 10) + 0xD800, code % 0x400 + 0xDC00)
62158 } return elements.join('');
62162 var quickselect$2 = createCommonjsModule(function (module, exports) {
62163 (function (global, factory) {
62164 module.exports = factory() ;
62165 })(commonjsGlobal, function () {
62167 function quickselect(arr, k, left, right, compare) {
62168 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
62171 function quickselectStep(arr, k, left, right, compare) {
62172 while (right > left) {
62173 if (right - left > 600) {
62174 var n = right - left + 1;
62175 var m = k - left + 1;
62176 var z = Math.log(n);
62177 var s = 0.5 * Math.exp(2 * z / 3);
62178 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
62179 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
62180 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
62181 quickselectStep(arr, k, newLeft, newRight, compare);
62187 swap(arr, left, k);
62188 if (compare(arr[right], t) > 0) swap(arr, left, right);
62195 while (compare(arr[i], t) < 0) {
62199 while (compare(arr[j], t) > 0) {
62204 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
62206 swap(arr, j, right);
62208 if (j <= k) left = j + 1;
62209 if (k <= j) right = j - 1;
62213 function swap(arr, i, j) {
62219 function defaultCompare(a, b) {
62220 return a < b ? -1 : a > b ? 1 : 0;
62223 return quickselect;
62227 var rbush_1 = rbush;
62228 var _default$2 = rbush;
62230 function rbush(maxEntries, format) {
62231 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
62233 this._maxEntries = Math.max(4, maxEntries || 9);
62234 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
62237 this._initFormat(format);
62243 rbush.prototype = {
62244 all: function all() {
62245 return this._all(this.data, []);
62247 search: function search(bbox) {
62248 var node = this.data,
62250 toBBox = this.toBBox;
62251 if (!intersects$1(bbox, node)) return result;
62252 var nodesToSearch = [],
62259 for (i = 0, len = node.children.length; i < len; i++) {
62260 child = node.children[i];
62261 childBBox = node.leaf ? toBBox(child) : child;
62263 if (intersects$1(bbox, childBBox)) {
62264 if (node.leaf) result.push(child);else if (contains$1(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
62268 node = nodesToSearch.pop();
62273 collides: function collides(bbox) {
62274 var node = this.data,
62275 toBBox = this.toBBox;
62276 if (!intersects$1(bbox, node)) return false;
62277 var nodesToSearch = [],
62284 for (i = 0, len = node.children.length; i < len; i++) {
62285 child = node.children[i];
62286 childBBox = node.leaf ? toBBox(child) : child;
62288 if (intersects$1(bbox, childBBox)) {
62289 if (node.leaf || contains$1(bbox, childBBox)) return true;
62290 nodesToSearch.push(child);
62294 node = nodesToSearch.pop();
62299 load: function load(data) {
62300 if (!(data && data.length)) return this;
62302 if (data.length < this._minEntries) {
62303 for (var i = 0, len = data.length; i < len; i++) {
62304 this.insert(data[i]);
62308 } // recursively build the tree with the given data from scratch using OMT algorithm
62311 var node = this._build(data.slice(), 0, data.length - 1, 0);
62313 if (!this.data.children.length) {
62314 // save as is if tree is empty
62316 } else if (this.data.height === node.height) {
62317 // split root if trees have the same height
62318 this._splitRoot(this.data, node);
62320 if (this.data.height < node.height) {
62321 // swap trees if inserted one is bigger
62322 var tmpNode = this.data;
62325 } // insert the small tree into the large tree at appropriate level
62328 this._insert(node, this.data.height - node.height - 1, true);
62333 insert: function insert(item) {
62334 if (item) this._insert(item, this.data.height - 1);
62337 clear: function clear() {
62338 this.data = createNode$1([]);
62341 remove: function remove(item, equalsFn) {
62342 if (!item) return this;
62343 var node = this.data,
62344 bbox = this.toBBox(item),
62350 goingUp; // depth-first iterative tree traversal
62352 while (node || path.length) {
62356 parent = path[path.length - 1];
62362 // check current node
62363 index = findItem$1(item, node.children, equalsFn);
62365 if (index !== -1) {
62366 // item found, remove the item and condense tree upwards
62367 node.children.splice(index, 1);
62370 this._condense(path);
62376 if (!goingUp && !node.leaf && contains$1(node, bbox)) {
62382 node = node.children[0];
62383 } else if (parent) {
62386 node = parent.children[i];
62388 } else node = null; // nothing found
62394 toBBox: function toBBox(item) {
62397 compareMinX: compareNodeMinX$1,
62398 compareMinY: compareNodeMinY$1,
62399 toJSON: function toJSON() {
62402 fromJSON: function fromJSON(data) {
62406 _all: function _all(node, result) {
62407 var nodesToSearch = [];
62410 if (node.leaf) result.push.apply(result, node.children);else nodesToSearch.push.apply(nodesToSearch, node.children);
62411 node = nodesToSearch.pop();
62416 _build: function _build(items, left, right, height) {
62417 var N = right - left + 1,
62418 M = this._maxEntries,
62422 // reached leaf level; return leaf
62423 node = createNode$1(items.slice(left, right + 1));
62424 calcBBox$1(node, this.toBBox);
62429 // target height of the bulk-loaded tree
62430 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
62432 M = Math.ceil(N / Math.pow(M, height - 1));
62435 node = createNode$1([]);
62437 node.height = height; // split the items into M mostly square tiles
62439 var N2 = Math.ceil(N / M),
62440 N1 = N2 * Math.ceil(Math.sqrt(M)),
62445 multiSelect$1(items, left, right, N1, this.compareMinX);
62447 for (i = left; i <= right; i += N1) {
62448 right2 = Math.min(i + N1 - 1, right);
62449 multiSelect$1(items, i, right2, N2, this.compareMinY);
62451 for (j = i; j <= right2; j += N2) {
62452 right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
62454 node.children.push(this._build(items, j, right3, height - 1));
62458 calcBBox$1(node, this.toBBox);
62461 _chooseSubtree: function _chooseSubtree(bbox, node, level, path) {
62462 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
62466 if (node.leaf || path.length - 1 === level) break;
62467 minArea = minEnlargement = Infinity;
62469 for (i = 0, len = node.children.length; i < len; i++) {
62470 child = node.children[i];
62471 area = bboxArea$1(child);
62472 enlargement = enlargedArea$1(bbox, child) - area; // choose entry with the least area enlargement
62474 if (enlargement < minEnlargement) {
62475 minEnlargement = enlargement;
62476 minArea = area < minArea ? area : minArea;
62477 targetNode = child;
62478 } else if (enlargement === minEnlargement) {
62479 // otherwise choose one with the smallest area
62480 if (area < minArea) {
62482 targetNode = child;
62487 node = targetNode || node.children[0];
62492 _insert: function _insert(item, level, isNode) {
62493 var toBBox = this.toBBox,
62494 bbox = isNode ? item : toBBox(item),
62495 insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
62497 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
62500 node.children.push(item);
62501 extend$3(node, bbox); // split on node overflow; propagate upwards if necessary
62503 while (level >= 0) {
62504 if (insertPath[level].children.length > this._maxEntries) {
62505 this._split(insertPath, level);
62509 } // adjust bboxes along the insertion path
62512 this._adjustParentBBoxes(bbox, insertPath, level);
62514 // split overflowed node into two
62515 _split: function _split(insertPath, level) {
62516 var node = insertPath[level],
62517 M = node.children.length,
62518 m = this._minEntries;
62520 this._chooseSplitAxis(node, m, M);
62522 var splitIndex = this._chooseSplitIndex(node, m, M);
62524 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
62525 newNode.height = node.height;
62526 newNode.leaf = node.leaf;
62527 calcBBox$1(node, this.toBBox);
62528 calcBBox$1(newNode, this.toBBox);
62529 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
62531 _splitRoot: function _splitRoot(node, newNode) {
62533 this.data = createNode$1([node, newNode]);
62534 this.data.height = node.height + 1;
62535 this.data.leaf = false;
62536 calcBBox$1(this.data, this.toBBox);
62538 _chooseSplitIndex: function _chooseSplitIndex(node, m, M) {
62539 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
62540 minOverlap = minArea = Infinity;
62542 for (i = m; i <= M - m; i++) {
62543 bbox1 = distBBox$1(node, 0, i, this.toBBox);
62544 bbox2 = distBBox$1(node, i, M, this.toBBox);
62545 overlap = intersectionArea$1(bbox1, bbox2);
62546 area = bboxArea$1(bbox1) + bboxArea$1(bbox2); // choose distribution with minimum overlap
62548 if (overlap < minOverlap) {
62549 minOverlap = overlap;
62551 minArea = area < minArea ? area : minArea;
62552 } else if (overlap === minOverlap) {
62553 // otherwise choose distribution with minimum area
62554 if (area < minArea) {
62563 // sorts node children by the best axis for split
62564 _chooseSplitAxis: function _chooseSplitAxis(node, m, M) {
62565 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
62566 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
62567 xMargin = this._allDistMargin(node, m, M, compareMinX),
62568 yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
62569 // otherwise it's already sorted by minY
62572 if (xMargin < yMargin) node.children.sort(compareMinX);
62574 // total margin of all possible split distributions where each node is at least m full
62575 _allDistMargin: function _allDistMargin(node, m, M, compare) {
62576 node.children.sort(compare);
62577 var toBBox = this.toBBox,
62578 leftBBox = distBBox$1(node, 0, m, toBBox),
62579 rightBBox = distBBox$1(node, M - m, M, toBBox),
62580 margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
62584 for (i = m; i < M - m; i++) {
62585 child = node.children[i];
62586 extend$3(leftBBox, node.leaf ? toBBox(child) : child);
62587 margin += bboxMargin$1(leftBBox);
62590 for (i = M - m - 1; i >= m; i--) {
62591 child = node.children[i];
62592 extend$3(rightBBox, node.leaf ? toBBox(child) : child);
62593 margin += bboxMargin$1(rightBBox);
62598 _adjustParentBBoxes: function _adjustParentBBoxes(bbox, path, level) {
62599 // adjust bboxes along the given tree path
62600 for (var i = level; i >= 0; i--) {
62601 extend$3(path[i], bbox);
62604 _condense: function _condense(path) {
62605 // go through the path, removing empty nodes and updating bboxes
62606 for (var i = path.length - 1, siblings; i >= 0; i--) {
62607 if (path[i].children.length === 0) {
62609 siblings = path[i - 1].children;
62610 siblings.splice(siblings.indexOf(path[i]), 1);
62611 } else this.clear();
62612 } else calcBBox$1(path[i], this.toBBox);
62615 _initFormat: function _initFormat(format) {
62616 // data format (minX, minY, maxX, maxY accessors)
62617 // uses eval-type function compilation instead of just accepting a toBBox function
62618 // because the algorithms are very sensitive to sorting functions performance,
62619 // so they should be dead simple and without inner calls
62620 var compareArr = ['return a', ' - b', ';'];
62621 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
62622 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
62623 this.toBBox = new Function('a', 'return {minX: a' + format[0] + ', minY: a' + format[1] + ', maxX: a' + format[2] + ', maxY: a' + format[3] + '};');
62627 function findItem$1(item, items, equalsFn) {
62628 if (!equalsFn) return items.indexOf(item);
62630 for (var i = 0; i < items.length; i++) {
62631 if (equalsFn(item, items[i])) return i;
62635 } // calculate node's bbox from bboxes of its children
62638 function calcBBox$1(node, toBBox) {
62639 distBBox$1(node, 0, node.children.length, toBBox, node);
62640 } // min bounding rectangle of node children from k to p-1
62643 function distBBox$1(node, k, p, toBBox, destNode) {
62644 if (!destNode) destNode = createNode$1(null);
62645 destNode.minX = Infinity;
62646 destNode.minY = Infinity;
62647 destNode.maxX = -Infinity;
62648 destNode.maxY = -Infinity;
62650 for (var i = k, child; i < p; i++) {
62651 child = node.children[i];
62652 extend$3(destNode, node.leaf ? toBBox(child) : child);
62658 function extend$3(a, b) {
62659 a.minX = Math.min(a.minX, b.minX);
62660 a.minY = Math.min(a.minY, b.minY);
62661 a.maxX = Math.max(a.maxX, b.maxX);
62662 a.maxY = Math.max(a.maxY, b.maxY);
62666 function compareNodeMinX$1(a, b) {
62667 return a.minX - b.minX;
62670 function compareNodeMinY$1(a, b) {
62671 return a.minY - b.minY;
62674 function bboxArea$1(a) {
62675 return (a.maxX - a.minX) * (a.maxY - a.minY);
62678 function bboxMargin$1(a) {
62679 return a.maxX - a.minX + (a.maxY - a.minY);
62682 function enlargedArea$1(a, b) {
62683 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));
62686 function intersectionArea$1(a, b) {
62687 var minX = Math.max(a.minX, b.minX),
62688 minY = Math.max(a.minY, b.minY),
62689 maxX = Math.min(a.maxX, b.maxX),
62690 maxY = Math.min(a.maxY, b.maxY);
62691 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
62694 function contains$1(a, b) {
62695 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
62698 function intersects$1(a, b) {
62699 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
62702 function createNode$1(children) {
62704 children: children,
62712 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
62713 // combines selection algorithm with binary divide & conquer approach
62716 function multiSelect$1(arr, left, right, n, compare) {
62717 var stack = [left, right],
62720 while (stack.length) {
62721 right = stack.pop();
62722 left = stack.pop();
62723 if (right - left <= n) continue;
62724 mid = left + Math.ceil((right - left) / n / 2) * n;
62725 quickselect$2(arr, mid, left, right, compare);
62726 stack.push(left, mid, mid, right);
62729 rbush_1["default"] = _default$2;
62731 var lineclip_1$1 = lineclip$1;
62732 lineclip$1.polyline = lineclip$1;
62733 lineclip$1.polygon = polygonclip$1; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
62734 // handle polylines rather than just segments
62736 function lineclip$1(points, bbox, result) {
62737 var len = points.length,
62738 codeA = bitCode$1(points[0], bbox),
62745 if (!result) result = [];
62747 for (i = 1; i < len; i++) {
62750 codeB = lastCode = bitCode$1(b, bbox);
62753 if (!(codeA | codeB)) {
62757 if (codeB !== lastCode) {
62758 // segment went outside
62762 // start a new line
62766 } else if (i === len - 1) {
62771 } else if (codeA & codeB) {
62774 } else if (codeA) {
62775 // a outside, intersect with clip edge
62776 a = intersect$1(a, b, codeA, bbox);
62777 codeA = bitCode$1(a, bbox);
62780 b = intersect$1(a, b, codeB, bbox);
62781 codeB = bitCode$1(b, bbox);
62788 if (part.length) result.push(part);
62790 } // Sutherland-Hodgeman polygon clipping algorithm
62793 function polygonclip$1(points, bbox) {
62794 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
62796 for (edge = 1; edge <= 8; edge *= 2) {
62798 prev = points[points.length - 1];
62799 prevInside = !(bitCode$1(prev, bbox) & edge);
62801 for (i = 0; i < points.length; i++) {
62803 inside = !(bitCode$1(p, bbox) & edge); // if segment goes through the clip window, add an intersection
62805 if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
62806 if (inside) result.push(p); // add a point if it's inside
62809 prevInside = inside;
62813 if (!points.length) break;
62817 } // intersect a segment against one of the 4 lines that make up the bbox
62820 function intersect$1(a, b, edge, bbox) {
62821 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
62822 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
62823 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
62824 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
62826 } // bit code reflects the point position relative to the bbox:
62828 // top 1001 1000 1010
62829 // mid 0001 0000 0010
62830 // bottom 0101 0100 0110
62833 function bitCode$1(p, bbox) {
62835 if (p[0] < bbox[0]) code |= 1; // left
62836 else if (p[0] > bbox[2]) code |= 2; // right
62838 if (p[1] < bbox[1]) code |= 4; // bottom
62839 else if (p[1] > bbox[3]) code |= 8; // top
62844 var whichPolygon_1 = whichPolygon;
62846 function whichPolygon(data) {
62849 for (var i = 0; i < data.features.length; i++) {
62850 var feature = data.features[i];
62851 var coords = feature.geometry.coordinates;
62853 if (feature.geometry.type === 'Polygon') {
62854 bboxes.push(treeItem(coords, feature.properties));
62855 } else if (feature.geometry.type === 'MultiPolygon') {
62856 for (var j = 0; j < coords.length; j++) {
62857 bboxes.push(treeItem(coords[j], feature.properties));
62862 var tree = rbush_1().load(bboxes);
62864 function query(p, multi) {
62866 result = tree.search({
62873 for (var i = 0; i < result.length; i++) {
62874 if (insidePolygon(result[i].coords, p)) {
62875 if (multi) output.push(result[i].props);else return result[i].props;
62879 return multi && output.length ? output : null;
62884 query.bbox = function queryBBox(bbox) {
62886 var result = tree.search({
62893 for (var i = 0; i < result.length; i++) {
62894 if (polygonIntersectsBBox(result[i].coords, bbox)) {
62895 output.push(result[i].props);
62905 function polygonIntersectsBBox(polygon, bbox) {
62906 var bboxCenter = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
62907 if (insidePolygon(polygon, bboxCenter)) return true;
62909 for (var i = 0; i < polygon.length; i++) {
62910 if (lineclip_1$1(polygon[i], bbox).length > 0) return true;
62914 } // ray casting algorithm for detecting if point is in polygon
62917 function insidePolygon(rings, p) {
62918 var inside = false;
62920 for (var i = 0, len = rings.length; i < len; i++) {
62921 var ring = rings[i];
62923 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
62924 if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
62931 function rayIntersect(p, p1, p2) {
62932 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];
62935 function treeItem(coords, props) {
62945 for (var i = 0; i < coords[0].length; i++) {
62946 var p = coords[0][i];
62947 item.minX = Math.min(item.minX, p[0]);
62948 item.minY = Math.min(item.minY, p[1]);
62949 item.maxX = Math.max(item.maxX, p[0]);
62950 item.maxY = Math.max(item.maxY, p[1]);
62956 var type = "FeatureCollection";
62957 var features = [{type:"Feature",properties:{m49:"680",wikidata:"Q3405693",nameEn:"Sark",country:"GB",groups:["GG","830","154","150"],level:"subterritory",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01481"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.36485,49.48223],[-2.65349,49.15373],[-2.09454,49.46288],[-2.36485,49.48223]]]]}},{type:"Feature",properties:{m49:"001",wikidata:"Q2",nameEn:"World",aliases:["Earth","Planet"],level:"world"},geometry:null},{type:"Feature",properties:{m49:"142",wikidata:"Q48",nameEn:"Asia",level:"region"},geometry:null},{type:"Feature",properties:{m49:"143",wikidata:"Q27275",nameEn:"Central Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"145",wikidata:"Q27293",nameEn:"Western Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"150",wikidata:"Q46",nameEn:"Europe",level:"region"},geometry:null},{type:"Feature",properties:{m49:"151",wikidata:"Q27468",nameEn:"Eastern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"154",wikidata:"Q27479",nameEn:"Northern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"155",wikidata:"Q27496",nameEn:"Western Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"202",wikidata:"Q132959",nameEn:"Sub-Saharan Africa",groups:["002"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"419",wikidata:"Q72829598",nameEn:"Latin America and the Caribbean",groups:["019"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"830",wikidata:"Q42314",nameEn:"Channel Islands",groups:["150","154"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"019",wikidata:"Q828",nameEn:"Americas",level:"region"},geometry:null},{type:"Feature",properties:{m49:"029",wikidata:"Q664609",nameEn:"Caribbean",groups:["419","019","003"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"034",wikidata:"Q771405",nameEn:"Southern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"002",wikidata:"Q15",nameEn:"Africa",level:"region"},geometry:null},{type:"Feature",properties:{m49:"003",wikidata:"Q49",nameEn:"North America",groups:["019"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"017",wikidata:"Q27433",nameEn:"Middle Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"039",wikidata:"Q27449",nameEn:"Southern Europe",groups:["150"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"005",wikidata:"Q18",nameEn:"South America",groups:["419","019"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"009",wikidata:"Q538",nameEn:"Oceania",level:"region"},geometry:null},{type:"Feature",properties:{m49:"061",wikidata:"Q35942",nameEn:"Polynesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"014",wikidata:"Q27407",nameEn:"Eastern Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"053",wikidata:"Q45256",nameEn:"Australia and New Zealand",aliases:["Australasia"],groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"011",wikidata:"Q4412",nameEn:"Western Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"013",wikidata:"Q27611",nameEn:"Central America",groups:["419","019","003"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"021",wikidata:"Q2017699",nameEn:"Northern America",groups:["019","003"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"035",wikidata:"Q11708",nameEn:"South-eastern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"018",wikidata:"Q27394",nameEn:"Southern Africa",groups:["202","002"],level:"intermediateRegion"},geometry:null},{type:"Feature",properties:{m49:"030",wikidata:"Q27231",nameEn:"Eastern Asia",groups:["142"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"015",wikidata:"Q27381",nameEn:"Northern Africa",groups:["002"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"054",wikidata:"Q37394",nameEn:"Melanesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{m49:"057",wikidata:"Q3359409",nameEn:"Micronesia",groups:["009"],level:"subregion"},geometry:null},{type:"Feature",properties:{iso1A2:"AC",iso1A3:"ASC",wikidata:"Q46197",nameEn:"Ascension Island",country:"GB",groups:["SH","011","202","002"],isoStatus:"excRes",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["247"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.82771,-8.70814],[-13.33271,-8.07391],[-14.91926,-6.63386],[-14.82771,-8.70814]]]]}},{type:"Feature",properties:{iso1A2:"AD",iso1A3:"AND",iso1N3:"020",wikidata:"Q228",nameEn:"Andorra",groups:["039","150"],callingCodes:["376"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AE",iso1A3:"ARE",iso1N3:"784",wikidata:"Q878",nameEn:"United Arab Emirates",groups:["145","142"],callingCodes:["971"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AF",iso1A3:"AFG",iso1N3:"004",wikidata:"Q889",nameEn:"Afghanistan",groups:["034","142"],callingCodes:["93"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AG",iso1A3:"ATG",iso1N3:"028",wikidata:"Q781",nameEn:"Antigua and Barbuda",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 268"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.12601,17.9235],[-62.27053,17.22145],[-62.62949,16.82364],[-62.52079,16.69392],[-62.14123,17.02632],[-61.83929,16.66647],[-61.44461,16.81958],[-61.45764,17.9187],[-62.12601,17.9235]]]]}},{type:"Feature",properties:{iso1A2:"AI",iso1A3:"AIA",iso1N3:"660",wikidata:"Q25228",nameEn:"Anguilla",country:"GB",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 264"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.83866,18.82518],[-63.35989,18.06012],[-62.86666,18.19278],[-62.75637,18.13489],[-62.46233,19.00569],[-63.83866,18.82518]]]]}},{type:"Feature",properties:{iso1A2:"AL",iso1A3:"ALB",iso1N3:"008",wikidata:"Q222",nameEn:"Albania",groups:["039","150"],callingCodes:["355"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AM",iso1A3:"ARM",iso1N3:"051",wikidata:"Q399",nameEn:"Armenia",groups:["145","142"],callingCodes:["374"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AO",iso1A3:"AGO",iso1N3:"024",wikidata:"Q916",nameEn:"Angola",groups:["017","202","002"],callingCodes:["244"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AQ",iso1A3:"ATA",iso1N3:"010",wikidata:"Q51",nameEn:"Antarctica",level:"region",callingCodes:["672"]},geometry:{type:"MultiPolygon",coordinates:[[[[180,-60],[-180,-60],[-180,-90],[180,-90],[180,-60]]]]}},{type:"Feature",properties:{iso1A2:"AR",iso1A3:"ARG",iso1N3:"032",wikidata:"Q414",nameEn:"Argentina",aliases:["RA"],groups:["005","419","019"],callingCodes:["54"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AS",iso1A3:"ASM",iso1N3:"016",wikidata:"Q16641",nameEn:"American Samoa",country:"US",groups:["061","009"],roadSpeedUnit:"mph",callingCodes:["1 684"]},geometry:{type:"MultiPolygon",coordinates:[[[[-174.18596,-12.48057],[-171.14953,-12.4725],[-171.14262,-14.93704],[-167.73854,-14.92809],[-167.75195,-10.12005],[-174.17993,-10.13616],[-174.18596,-12.48057]]]]}},{type:"Feature",properties:{iso1A2:"AT",iso1A3:"AUT",iso1N3:"040",wikidata:"Q40",nameEn:"Austria",groups:["EU","155","150"],callingCodes:["43"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AU",iso1A3:"AUS",iso1N3:"036",wikidata:"Q408",nameEn:"Australia",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[156.55918,-21.85134],[158.60851,-15.7108],[144.30183,-9.48146],[142.81927,-9.31709],[142.5723,-9.35994],[142.31447,-9.24611],[142.23304,-9.19253],[142.1462,-9.19923],[142.0953,-9.23534],[142.0601,-9.56571],[140.88922,-9.34945],[127.55165,-9.05052],[96.7091,-25.20343],[159.69067,-56.28945],[165.46901,-28.32101],[156.55918,-21.85134]]]]}},{type:"Feature",properties:{iso1A2:"AW",iso1A3:"ABW",iso1N3:"533",wikidata:"Q21203",nameEn:"Aruba",country:"NL",groups:["029","003","419","019"],callingCodes:["297"]},geometry:{type:"MultiPolygon",coordinates:[[[[-70.00823,12.98375],[-70.35625,12.58277],[-69.60231,12.17],[-70.00823,12.98375]]]]}},{type:"Feature",properties:{iso1A2:"AX",iso1A3:"ALA",iso1N3:"248",wikidata:"Q5689",nameEn:"Åland Islands",country:"FI",groups:["EU","154","150"],callingCodes:["358 18","358 457"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"AZ",iso1A3:"AZE",iso1N3:"031",wikidata:"Q227",nameEn:"Azerbaijan",groups:["145","142"],callingCodes:["994"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BA",iso1A3:"BIH",iso1N3:"070",wikidata:"Q225",nameEn:"Bosnia and Herzegovina",groups:["039","150"],callingCodes:["387"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BB",iso1A3:"BRB",iso1N3:"052",wikidata:"Q244",nameEn:"Barbados",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 246"]},geometry:{type:"MultiPolygon",coordinates:[[[[-58.56442,13.24471],[-59.80731,13.87556],[-60.19227,12.37597],[-58.56442,13.24471]]]]}},{type:"Feature",properties:{iso1A2:"BD",iso1A3:"BGD",iso1N3:"050",wikidata:"Q902",nameEn:"Bangladesh",groups:["034","142"],driveSide:"left",callingCodes:["880"]},geometry:{type:"MultiPolygon",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.03553,21.77397],[89.13927,21.60785],[89.13606,21.42955],[92.39837,20.38919],[92.4302,20.5688],[92.31348,20.57137],[92.28464,20.63179],[92.37665,20.72172],[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]]]]}},{type:"Feature",properties:{iso1A2:"BE",iso1A3:"BEL",iso1N3:"056",wikidata:"Q31",nameEn:"Belgium",groups:["EU","155","150"],callingCodes:["32"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BF",iso1A3:"BFA",iso1N3:"854",wikidata:"Q965",nameEn:"Burkina Faso",groups:["011","202","002"],callingCodes:["226"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BG",iso1A3:"BGR",iso1N3:"100",wikidata:"Q219",nameEn:"Bulgaria",groups:["EU","151","150"],callingCodes:["359"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BH",iso1A3:"BHR",iso1N3:"048",wikidata:"Q398",nameEn:"Bahrain",groups:["145","142"],callingCodes:["973"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BI",iso1A3:"BDI",iso1N3:"108",wikidata:"Q967",nameEn:"Burundi",groups:["014","202","002"],callingCodes:["257"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BJ",iso1A3:"BEN",iso1N3:"204",wikidata:"Q962",nameEn:"Benin",aliases:["DY"],groups:["011","202","002"],callingCodes:["229"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BL",iso1A3:"BLM",iso1N3:"652",wikidata:"Q25362",nameEn:"Saint-Barthélemy",country:"FR",groups:["029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.75637,18.13489],[-62.93924,18.02904],[-63.07669,17.79659],[-62.76692,17.64353],[-62.54836,17.8636],[-62.75637,18.13489]]]]}},{type:"Feature",properties:{iso1A2:"BM",iso1A3:"BMU",iso1N3:"060",wikidata:"Q23635",nameEn:"Bermuda",country:"GB",groups:["021","003","019"],driveSide:"left",callingCodes:["1 441"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.20987,32.6953],[-65.31453,32.68437],[-65.63955,31.43417],[-63.20987,32.6953]]]]}},{type:"Feature",properties:{iso1A2:"BN",iso1A3:"BRN",iso1N3:"096",wikidata:"Q921",nameEn:"Brunei",groups:["035","142"],driveSide:"left",callingCodes:["673"]},geometry:{type:"MultiPolygon",coordinates:[[[[115.16236,5.01011],[115.02521,5.35005],[114.08532,4.64632],[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]]]]}},{type:"Feature",properties:{iso1A2:"BO",iso1A3:"BOL",iso1N3:"068",wikidata:"Q750",nameEn:"Bolivia",groups:["005","419","019"],callingCodes:["591"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BQ",iso1A3:"BES",iso1N3:"535",wikidata:"Q27561",nameEn:"Caribbean Netherlands",country:"NL",groups:["029","003","419","019"],callingCodes:["599 3","599 4","599 7"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.07669,17.79659],[-63.22932,17.32592],[-63.11114,17.23125],[-62.76692,17.64353],[-63.07669,17.79659]]],[[[-63.29212,17.90532],[-63.58819,17.61311],[-63.22932,17.32592],[-63.07669,17.79659],[-63.29212,17.90532]]],[[[-67.89186,12.4116],[-68.90012,12.62309],[-68.33524,11.78151],[-68.01417,11.77722],[-67.89186,12.4116]]]]}},{type:"Feature",properties:{iso1A2:"BR",iso1A3:"BRA",iso1N3:"076",wikidata:"Q155",nameEn:"Brazil",groups:["005","419","019"],callingCodes:["55"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BS",iso1A3:"BHS",iso1N3:"044",wikidata:"Q778",nameEn:"The Bahamas",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 242"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.98446,20.4801],[-71.70065,25.7637],[-79.14818,27.83105],[-79.89631,24.6597],[-80.88924,23.80416],[-72.98446,20.4801]]]]}},{type:"Feature",properties:{iso1A2:"BT",iso1A3:"BTN",iso1N3:"064",wikidata:"Q917",nameEn:"Bhutan",groups:["034","142"],driveSide:"left",callingCodes:["975"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BV",iso1A3:"BVT",iso1N3:"074",wikidata:"Q23408",nameEn:"Bouvet Island",country:"NO",groups:["005","419","019"]},geometry:{type:"MultiPolygon",coordinates:[[[[4.54042,-54.0949],[2.28941,-54.13089],[3.35353,-55.17558],[4.54042,-54.0949]]]]}},{type:"Feature",properties:{iso1A2:"BW",iso1A3:"BWA",iso1N3:"072",wikidata:"Q963",nameEn:"Botswana",groups:["018","202","002"],driveSide:"left",callingCodes:["267"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BY",iso1A3:"BLR",iso1N3:"112",wikidata:"Q184",nameEn:"Belarus",groups:["151","150"],callingCodes:["375"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"BZ",iso1A3:"BLZ",iso1N3:"084",wikidata:"Q242",nameEn:"Belize",groups:["013","003","419","019"],roadSpeedUnit:"mph",callingCodes:["501"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CA",iso1A3:"CAN",iso1N3:"124",wikidata:"Q16",nameEn:"Canada",groups:["021","003","019"],callingCodes:["1"]},geometry:{type:"MultiPolygon",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],[-76.75614,76.72014],[-68.21821,80.48551],[-45.47832,84.58738],[-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],[-117.03266,49.00056],[-116.04938,48.99999],[-114.0683,48.99885],[-110.0051,48.99901],[-104.05004,48.99925],[-101.36198,48.99935],[-97.24024,48.99952],[-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]]]]}},{type:"Feature",properties:{iso1A2:"CC",iso1A3:"CCK",iso1N3:"166",wikidata:"Q36004",nameEn:"Cocos (Keeling) Islands",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[96.61846,-10.82438],[96.02343,-12.68334],[97.93979,-12.33309],[96.61846,-10.82438]]]]}},{type:"Feature",properties:{iso1A2:"CD",iso1A3:"COD",iso1N3:"180",wikidata:"Q974",nameEn:"Democratic Republic of the Congo",aliases:["ZR"],groups:["017","202","002"],callingCodes:["243"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CF",iso1A3:"CAF",iso1N3:"140",wikidata:"Q929",nameEn:"Central African Republic",groups:["017","202","002"],callingCodes:["236"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CG",iso1A3:"COG",iso1N3:"178",wikidata:"Q971",nameEn:"Republic of the Congo",groups:["017","202","002"],callingCodes:["242"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CH",iso1A3:"CHE",iso1N3:"756",wikidata:"Q39",nameEn:"Switzerland",groups:["155","150"],callingCodes:["41"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CI",iso1A3:"CIV",iso1N3:"384",wikidata:"Q1008",nameEn:"Côte d'Ivoire",groups:["011","202","002"],callingCodes:["225"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CK",iso1A3:"COK",iso1N3:"184",wikidata:"Q26988",nameEn:"Cook Islands",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["682"]},geometry:{type:"MultiPolygon",coordinates:[[[[-167.73854,-14.92809],[-167.73129,-23.22266],[-156.46451,-23.21255],[-156.4957,-12.32002],[-156.50903,-7.4975],[-167.75329,-7.52784],[-167.75195,-10.12005],[-167.73854,-14.92809]]]]}},{type:"Feature",properties:{iso1A2:"CL",iso1A3:"CHL",iso1N3:"152",wikidata:"Q298",nameEn:"Chile",groups:["005","419","019"],callingCodes:["56"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CM",iso1A3:"CMR",iso1N3:"120",wikidata:"Q1009",nameEn:"Cameroon",groups:["017","202","002"],callingCodes:["237"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CN",iso1A3:"CHN",iso1N3:"156",wikidata:"Q148",nameEn:"China",aliases:["RC"],groups:["030","142"],callingCodes:["86"]},geometry:{type:"MultiPolygon",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],[117.07142,49.68482],[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.08431,47.80693],[116.2527,47.87766],[116.4465,47.83662],[116.67405,47.89039],[116.87527,47.88836],[117.08918,47.82242],[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.37306,46.61132],[119.30261,46.6083],[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],[117.07252,46.35818],[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.91898,45.6227],[115.69688,45.45761],[115.35757,45.39106],[114.94546,45.37377],[114.74612,45.43585],[114.54801,45.38337],[114.5166,45.27189],[114.08071,44.92847],[113.909,44.91444],[113.63821,44.74326],[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],[108.23156,42.45532],[107.57258,42.40898],[107.49681,42.46221],[107.29755,42.41395],[107.24774,42.36107],[106.76517,42.28741],[105.24708,41.7442],[105.01119,41.58382],[104.91272,41.64619],[104.51667,41.66113],[104.52258,41.8706],[103.92804,41.78246],[103.3685,41.89696],[102.72403,42.14675],[102.42826,42.15137],[102.07645,42.22519],[101.80515,42.50074],[101.28833,42.58524],[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],[79.01931,31.42817],[79.14016,31.43403],[79.22805,31.34963],[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.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]]]]}},{type:"Feature",properties:{iso1A2:"CO",iso1A3:"COL",iso1N3:"170",wikidata:"Q739",nameEn:"Colombia",groups:["005","419","019"],callingCodes:["57"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CP",iso1A3:"CPT",wikidata:"Q161258",nameEn:"Clipperton Island",country:"FR",isoStatus:"excRes"},geometry:{type:"MultiPolygon",coordinates:[[[[-110.36279,9.79626],[-108.755,9.84085],[-109.04145,11.13245],[-110.36279,9.79626]]]]}},{type:"Feature",properties:{iso1A2:"CR",iso1A3:"CRI",iso1N3:"188",wikidata:"Q800",nameEn:"Costa Rica",groups:["013","003","419","019"],callingCodes:["506"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"CU",iso1A3:"CUB",iso1N3:"192",wikidata:"Q241",nameEn:"Cuba",groups:["029","003","419","019"],callingCodes:["53"]},geometry:{type:"MultiPolygon",coordinates:[[[[-73.62304,20.6935],[-82.02215,24.23074],[-85.77883,21.92705],[-74.81171,18.82201],[-73.62304,20.6935]]]]}},{type:"Feature",properties:{iso1A2:"CV",iso1A3:"CPV",iso1N3:"132",wikidata:"Q1011",nameEn:"Cape Verde",groups:["011","202","002"],callingCodes:["238"]},geometry:{type:"MultiPolygon",coordinates:[[[[-28.81604,14.57305],[-20.39702,14.12816],[-23.37101,19.134],[-28.81604,14.57305]]]]}},{type:"Feature",properties:{iso1A2:"CW",iso1A3:"CUW",iso1N3:"531",wikidata:"Q25279",nameEn:"Curaçao",country:"NL",groups:["029","003","419","019"],callingCodes:["599"]},geometry:{type:"MultiPolygon",coordinates:[[[[-68.90012,12.62309],[-69.59009,12.46019],[-68.99639,11.79035],[-68.33524,11.78151],[-68.90012,12.62309]]]]}},{type:"Feature",properties:{iso1A2:"CX",iso1A3:"CXR",iso1N3:"162",wikidata:"Q31063",nameEn:"Christmas Island",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["61"]},geometry:{type:"MultiPolygon",coordinates:[[[[105.66835,-9.31927],[104.67494,-11.2566],[106.66176,-11.14349],[105.66835,-9.31927]]]]}},{type:"Feature",properties:{iso1A2:"CY",iso1A3:"CYP",iso1N3:"196",wikidata:"Q229",nameEn:"Cyprus",groups:["EU","145","142"],driveSide:"left",callingCodes:["357"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.70639,34.99303],[33.71514,35.00294],[33.69731,35.01754],[33.69938,35.03123],[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],[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.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]]]]}},{type:"Feature",properties:{iso1A2:"CZ",iso1A3:"CZE",iso1N3:"203",wikidata:"Q213",nameEn:"Czechia",groups:["EU","151","150"],callingCodes:["420"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"DE",iso1A3:"DEU",iso1N3:"276",wikidata:"Q183",nameEn:"Germany",groups:["EU","155","150"],callingCodes:["49"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"DG",iso1A3:"DGA",wikidata:"Q184851",nameEn:"Diego Garcia",country:"GB",groups:["IO","014","202","002"],isoStatus:"excRes",callingCodes:["246"]},geometry:{type:"MultiPolygon",coordinates:[[[[73.14823,-7.76302],[73.09982,-6.07324],[71.43792,-7.73904],[73.14823,-7.76302]]]]}},{type:"Feature",properties:{iso1A2:"DJ",iso1A3:"DJI",iso1N3:"262",wikidata:"Q977",nameEn:"Djibouti",groups:["014","202","002"],callingCodes:["253"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.42425,11.70983],[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.42425,11.70983]]]]}},{type:"Feature",properties:{iso1A2:"DK",iso1A3:"DNK",iso1N3:"208",wikidata:"Q35",nameEn:"Denmark",groups:["EU","154","150"],callingCodes:["45"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"DM",iso1A3:"DMA",iso1N3:"212",wikidata:"Q784",nameEn:"Dominica",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 767"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.51867,14.96709],[-60.69955,15.22234],[-60.95725,15.70997],[-61.44899,15.79571],[-61.81728,15.58058],[-61.51867,14.96709]]]]}},{type:"Feature",properties:{iso1A2:"DO",iso1A3:"DOM",iso1N3:"214",wikidata:"Q786",nameEn:"Dominican Republic",groups:["029","003","419","019"],callingCodes:["1 809","1 829","1 849"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"DZ",iso1A3:"DZA",iso1N3:"012",wikidata:"Q262",nameEn:"Algeria",groups:["015","002"],callingCodes:["213"]},geometry:{type:"MultiPolygon",coordinates:[[[[8.59123,37.14286],[2.46645,37.97429],[-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]]]]}},{type:"Feature",properties:{iso1A2:"EA",wikidata:"Q28868874",nameEn:"Ceuta, Melilla",country:"ES",groups:["015","002"],isoStatus:"excRes",callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.38491,35.92591],[-5.37338,35.88417],[-5.35844,35.87375],[-5.34379,35.8711],[-5.27056,35.88794],[-5.27635,35.91222],[-5.38491,35.92591]]],[[[-2.92224,35.3401],[-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.92674,35.27313],[-2.92181,35.28599],[-2.92224,35.3401]]]]}},{type:"Feature",properties:{iso1A2:"EC",iso1A3:"ECU",iso1N3:"218",wikidata:"Q736",nameEn:"Ecuador",groups:["005","419","019"],callingCodes:["593"]},geometry:{type:"MultiPolygon",coordinates:[[[[-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],[-93.12365,2.64343],[-92.46744,-2.52874],[-80.30602,-3.39149],[-80.20647,-3.431],[-80.24123,-3.46124],[-80.24475,-3.47846],[-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]]]]}},{type:"Feature",properties:{iso1A2:"EE",iso1A3:"EST",iso1N3:"233",wikidata:"Q191",nameEn:"Estonia",aliases:["EW"],groups:["EU","154","150"],callingCodes:["372"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"EG",iso1A3:"EGY",iso1N3:"818",wikidata:"Q79",nameEn:"Egypt",groups:["015","002"],callingCodes:["20"]},geometry:{type:"MultiPolygon",coordinates:[[[[33.62659,31.82938],[25.63787,31.9359],[25.14001,31.67534],[25.06041,31.57937],[24.83101,31.31921],[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.51305,27.70027],[34.46254,27.99552],[34.88293,29.37455],[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]]]]}},{type:"Feature",properties:{iso1A2:"EH",iso1A3:"ESH",iso1N3:"732",wikidata:"Q6250",nameEn:"Western Sahara",groups:["015","002"],callingCodes:["212"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"ER",iso1A3:"ERI",iso1N3:"232",wikidata:"Q986",nameEn:"Eritrea",groups:["014","202","002"],callingCodes:["291"]},geometry:{type:"MultiPolygon",coordinates:[[[[41.37609,16.19728],[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],[42.63806,13.58268],[41.29956,15.565],[41.37609,16.19728]]]]}},{type:"Feature",properties:{iso1A2:"ES",iso1A3:"ESP",iso1N3:"724",wikidata:"Q29",nameEn:"Spain",groups:["EU","039","150"],callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.41312,35.17111],[-2.41265,35.1877],[-2.44896,35.18777],[-2.44887,35.17075],[-2.41312,35.17111]]],[[[-3.90602,35.21494],[-3.88926,35.20841],[-3.88617,35.21406],[-3.90288,35.22024],[-3.90602,35.21494]]],[[[-4.30191,35.17419],[-4.30112,35.17058],[-4.29436,35.17149],[-4.30191,35.17419]]],[[[-7.27694,35.93599],[-5.64962,35.93752],[-5.10878,36.05227],[-2.85819,35.63219],[-2.27707,35.35051],[2.46645,37.97429],[5.18061,39.43581],[3.4481,42.4358],[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],[-9.14112,41.86623],[-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.27694,35.93599]],[[-5.28217,36.09907],[-5.3004,36.07439],[-5.32837,36.05935],[-5.36503,36.06205],[-5.39074,36.10278],[-5.40134,36.14896],[-5.38545,36.15481],[-5.36494,36.15496],[-5.34536,36.15501],[-5.33822,36.15272],[-5.27801,36.14942],[-5.28217,36.09907]]],[[[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]]]]}},{type:"Feature",properties:{iso1A2:"ET",iso1A3:"ETH",iso1N3:"231",wikidata:"Q115",nameEn:"Ethiopia",groups:["014","202","002"],callingCodes:["251"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"EU",iso1A3:"EUE",wikidata:"Q458",nameEn:"European Union",level:"union",isoStatus:"excRes"},geometry:null},{type:"Feature",properties:{iso1A2:"FI",iso1A3:"FIN",iso1N3:"246",wikidata:"Q33",nameEn:"Finland",aliases:["SF"],groups:["EU","154","150"],callingCodes:["358"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"FJ",iso1A3:"FJI",iso1N3:"242",wikidata:"Q712",nameEn:"Fiji",groups:["054","009"],driveSide:"left",callingCodes:["679"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-22.5],[179.99999,-22.5],[179.99999,-11.5],[174,-11.5],[174,-22.5]]],[[[-178.60161,-14.95666],[-180,-14.96041],[-180,-22.90585],[-176.74538,-22.89767],[-176.76826,-14.95183],[-178.60161,-14.95666]]]]}},{type:"Feature",properties:{iso1A2:"FK",iso1A3:"FLK",iso1N3:"238",wikidata:"Q9648",nameEn:"Falkland Islands",country:"GB",groups:["005","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["500"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.67376,-55.11859],[-54.56126,-51.26248],[-61.26735,-50.63919],[-63.67376,-55.11859]]]]}},{type:"Feature",properties:{iso1A2:"FM",iso1A3:"FSM",iso1N3:"583",wikidata:"Q702",nameEn:"Federated States of Micronesia",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["691"]},geometry:{type:"MultiPolygon",coordinates:[[[[136.04605,12.45908],[136.27107,6.73747],[156.88247,-1.39237],[165.35175,6.367],[159.04653,10.59067],[136.04605,12.45908]]]]}},{type:"Feature",properties:{iso1A2:"FO",iso1A3:"FRO",iso1N3:"234",wikidata:"Q4628",nameEn:"Faroe Islands",country:"DK",groups:["154","150"],callingCodes:["298"]},geometry:{type:"MultiPolygon",coordinates:[[[[-8.51774,62.35338],[-6.51083,60.95272],[-5.70102,62.77194],[-8.51774,62.35338]]]]}},{type:"Feature",properties:{iso1A2:"FR",iso1A3:"FRA",iso1N3:"250",wikidata:"Q142",nameEn:"France",groups:["EU","155","150"],callingCodes:["33"]},geometry:null},{type:"Feature",properties:{iso1A2:"FX",iso1A3:"FXX",iso1N3:"249",wikidata:"Q212429",nameEn:"Metropolitan France",country:"FR",groups:["EU","155","150"],isoStatus:"excRes",callingCodes:["33"]},geometry:{type:"MultiPolygon",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.13339,48.73907],[-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.4481,42.4358],[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]]]]}},{type:"Feature",properties:{iso1A2:"GA",iso1A3:"GAB",iso1N3:"266",wikidata:"Q1000",nameEn:"Gabon",groups:["017","202","002"],callingCodes:["241"]},geometry:{type:"MultiPolygon",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.78058,1.03996],[9.76085,1.05949],[9.73014,1.06721],[9.68638,1.06836],[9.66092,1.05865],[9.62096,1.03039],[9.54793,1.0185],[9.51998,0.96418],[9.35563,0.84865],[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]]]]}},{type:"Feature",properties:{iso1A2:"GB",iso1A3:"GBR",iso1N3:"826",wikidata:"Q145",nameEn:"United Kingdom",aliases:["UK","Britain","Great Britain"],groups:["154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.83481,53.87749],[-4.1819,54.57861],[-3.64906,54.12723],[-5.37267,53.63269],[-5.79914,52.03902],[-7.74976,48.64773],[1.17405,50.74239],[2.18458,51.52087],[2.56575,51.85301],[-0.3751,61.32236],[-14.78497,57.60709],[-7.93366,55.84142],[-6.79943,55.54107],[-6.71944,55.27952],[-6.9734,55.19878],[-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]]],[[[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]]],[[[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]]]]}},{type:"Feature",properties:{iso1A2:"GD",iso1A3:"GRD",iso1N3:"308",wikidata:"Q769",nameEn:"Grenada",aliases:["WG"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 473"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.14806,11.87638],[-61.57265,11.65795],[-61.13395,12.51526],[-61.38256,12.52991],[-61.73897,12.61191],[-62.14806,11.87638]]]]}},{type:"Feature",properties:{iso1A2:"GE",iso1A3:"GEO",iso1N3:"268",wikidata:"Q230",nameEn:"Georgia",groups:["145","142"],callingCodes:["995"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"GF",iso1A3:"GUF",iso1N3:"254",wikidata:"Q3769",nameEn:"French Guiana",country:"FR",groups:["EU","005","419","019"],callingCodes:["594"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"GG",iso1A3:"GGY",iso1N3:"831",wikidata:"Q25230",nameEn:"Guernsey",country:"GB",groups:["830","154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01481"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.65349,49.15373],[-2.36485,49.48223],[-2.09454,49.46288],[-2.02963,49.91866],[-3.28154,49.57329],[-2.65349,49.15373]]]]}},{type:"Feature",properties:{iso1A2:"GH",iso1A3:"GHA",iso1N3:"288",wikidata:"Q117",nameEn:"Ghana",groups:["011","202","002"],callingCodes:["233"]},geometry:{type:"MultiPolygon",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],[-0.00908,10.91644],[-0.0063,10.96417],[0.03355,10.9807],[0.02395,11.06229],[0.00342,11.08317],[-0.00514,11.10763],[-0.0275,11.11202],[-0.05733,11.08628],[-0.14462,11.10811],[-0.13493,11.14075]]]]}},{type:"Feature",properties:{iso1A2:"GI",iso1A3:"GIB",iso1N3:"292",wikidata:"Q1410",nameEn:"Gibraltar",country:"GB",groups:["039","150"],callingCodes:["350"]},geometry:{type:"MultiPolygon",coordinates:[[[[-5.28217,36.09907],[-5.27801,36.14942],[-5.33822,36.15272],[-5.34536,36.15501],[-5.36494,36.15496],[-5.38545,36.15481],[-5.40134,36.14896],[-5.39074,36.10278],[-5.36503,36.06205],[-5.32837,36.05935],[-5.3004,36.07439],[-5.28217,36.09907]]]]}},{type:"Feature",properties:{iso1A2:"GL",iso1A3:"GRL",iso1N3:"304",wikidata:"Q223",nameEn:"Greenland",country:"DK",groups:["021","003","019"],callingCodes:["299"]},geometry:{type:"MultiPolygon",coordinates:[[[[-45.47832,84.58738],[-68.21821,80.48551],[-76.75614,76.72014],[-46.37635,57.3249],[-9.68082,72.73731],[-5.7106,84.28058],[-45.47832,84.58738]]]]}},{type:"Feature",properties:{iso1A2:"GM",iso1A3:"GMB",iso1N3:"270",wikidata:"Q1005",nameEn:"The Gambia",groups:["011","202","002"],callingCodes:["220"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"GN",iso1A3:"GIN",iso1N3:"324",wikidata:"Q1006",nameEn:"Guinea",groups:["011","202","002"],callingCodes:["224"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"GP",iso1A3:"GLP",iso1N3:"312",wikidata:"Q17012",nameEn:"Guadeloupe",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.95725,15.70997],[-60.71337,16.48911],[-61.44461,16.81958],[-61.83929,16.66647],[-62.17275,16.35721],[-61.81728,15.58058],[-61.44899,15.79571],[-60.95725,15.70997]]]]}},{type:"Feature",properties:{iso1A2:"GQ",iso1A3:"GNQ",iso1N3:"226",wikidata:"Q983",nameEn:"Equatorial Guinea",groups:["017","202","002"],callingCodes:["240"]},geometry:{type:"MultiPolygon",coordinates:[[[[9.22018,3.72052],[8.34397,4.30689],[8.05799,3.48284],[8.0168,1.79377],[6.69416,-0.53945],[5.38965,-1.19244],[5.3459,-2.30107],[7.24416,-0.64092],[9.35563,0.84865],[9.51998,0.96418],[9.54793,1.0185],[9.62096,1.03039],[9.66092,1.05865],[9.68638,1.06836],[9.73014,1.06721],[9.76085,1.05949],[9.78058,1.03996],[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]]]]}},{type:"Feature",properties:{iso1A2:"GR",iso1A3:"GRC",iso1N3:"300",wikidata:"Q41",nameEn:"Greece",aliases:["EL"],groups:["EU","039","150"],callingCodes:["30"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"GS",iso1A3:"SGS",iso1N3:"239",wikidata:"Q35086",nameEn:"South Georgia and South Sandwich Islands",country:"GB",groups:["005","419","019"],driveSide:"left",callingCodes:["500"]},geometry:{type:"MultiPolygon",coordinates:[[[[-35.26394,-43.68272],[-53.39656,-59.87088],[-22.31757,-59.85974],[-35.26394,-43.68272]]]]}},{type:"Feature",properties:{iso1A2:"GT",iso1A3:"GTM",iso1N3:"320",wikidata:"Q774",nameEn:"Guatemala",groups:["013","003","419","019"],callingCodes:["502"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"GU",iso1A3:"GUM",iso1N3:"316",wikidata:"Q16635",nameEn:"Guam",country:"US",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["1 671"]},geometry:{type:"MultiPolygon",coordinates:[[[[146.25931,13.85876],[143.82485,13.92273],[144.61642,12.82462],[146.25931,13.85876]]]]}},{type:"Feature",properties:{iso1A2:"GW",iso1A3:"GNB",iso1N3:"624",wikidata:"Q1007",nameEn:"Guinea-Bissau",groups:["011","202","002"],callingCodes:["245"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"GY",iso1A3:"GUY",iso1N3:"328",wikidata:"Q734",nameEn:"Guyana",groups:["005","419","019"],driveSide:"left",callingCodes:["592"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"HK",iso1A3:"HKG",iso1N3:"344",wikidata:"Q8646",nameEn:"Hong Kong",country:"CN",groups:["030","142"],driveSide:"left",callingCodes:["852"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"HM",iso1A3:"HMD",iso1N3:"334",wikidata:"Q131198",nameEn:"Heard Island and McDonald Islands",country:"AU",groups:["053","009"],driveSide:"left"},geometry:{type:"MultiPolygon",coordinates:[[[[71.08716,-53.87687],[75.44182,-53.99822],[72.87012,-51.48322],[71.08716,-53.87687]]]]}},{type:"Feature",properties:{iso1A2:"HN",iso1A3:"HND",iso1N3:"340",wikidata:"Q783",nameEn:"Honduras",groups:["013","003","419","019"],callingCodes:["504"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"HR",iso1A3:"HRV",iso1N3:"191",wikidata:"Q224",nameEn:"Croatia",groups:["EU","039","150"],callingCodes:["385"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"HT",iso1A3:"HTI",iso1N3:"332",wikidata:"Q790",nameEn:"Haiti",aliases:["RH"],groups:["029","003","419","019"],callingCodes:["509"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"HU",iso1A3:"HUN",iso1N3:"348",wikidata:"Q28",nameEn:"Hungary",groups:["EU","151","150"],callingCodes:["36"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"IC",wikidata:"Q5813",nameEn:"Canary Islands",country:"ES",groups:["EU","039","150"],isoStatus:"excRes",callingCodes:["34"]},geometry:{type:"MultiPolygon",coordinates:[[[[-15.92339,29.50503],[-25.3475,27.87574],[-14.43883,27.02969],[-9.94494,32.97138],[-15.92339,29.50503]]]]}},{type:"Feature",properties:{iso1A2:"ID",iso1A3:"IDN",iso1N3:"360",wikidata:"Q252",nameEn:"Indonesia",aliases:["RI"],groups:["035","142"],driveSide:"left",callingCodes:["62"]},geometry:{type:"MultiPolygon",coordinates:[[[[141.02352,0.08993],[128.97621,3.08804],[126.69413,6.02692],[124.97752,4.82064],[118.41402,3.99509],[118.07935,4.15511],[117.89538,4.16637],[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.64506,2.08014],[109.71058,2.32059],[108.10426,5.42408],[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],[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.31127,-8.22976],[125.65946,-8.06136],[125.87691,-8.31789],[127.42116,-8.22471],[127.55165,-9.05052],[140.88922,-9.34945],[141.00782,-9.1242],[141.01763,-6.90181],[140.85295,-6.72996],[140.99813,-6.3233],[141.02352,0.08993]]]]}},{type:"Feature",properties:{iso1A2:"IE",iso1A3:"IRL",iso1N3:"372",wikidata:"Q27",nameEn:"Ireland",groups:["EU","154","150"],driveSide:"left",callingCodes:["353"]},geometry:{type:"MultiPolygon",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.9734,55.19878],[-6.71944,55.27952],[-6.79943,55.54107],[-7.93366,55.84142],[-22.01468,48.19557],[-5.79914,52.03902],[-5.37267,53.63269],[-5.83481,53.87749],[-6.26218,54.09785]]]]}},{type:"Feature",properties:{iso1A2:"IL",iso1A3:"ISR",iso1N3:"376",wikidata:"Q801",nameEn:"Israel",groups:["145","142"],callingCodes:["972"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"IM",iso1A3:"IMN",iso1N3:"833",wikidata:"Q9676",nameEn:"Isle of Man",country:"GB",groups:["154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01624","44 07624","44 07524","44 07924"]},geometry:{type:"MultiPolygon",coordinates:[[[[-3.64906,54.12723],[-4.1819,54.57861],[-5.83481,53.87749],[-5.37267,53.63269],[-3.64906,54.12723]]]]}},{type:"Feature",properties:{iso1A2:"IN",iso1A3:"IND",iso1N3:"356",wikidata:"Q668",nameEn:"India",groups:["034","142"],driveSide:"left",callingCodes:["91"]},geometry:{type:"MultiPolygon",coordinates:[[[[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],[72.15131,7.6285],[78.52781,7.63099],[79.50447,8.91876],[79.42124,9.80115],[80.48418,10.20786],[94.53911,5.99016],[94.6371,13.81803],[92.61042,13.76986],[89.13606,21.42955],[89.13927,21.60785],[89.03553,21.77397],[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.08507,28.38346],[80.89648,28.47237],[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.56957,29.88176],[80.60226,29.95732],[80.67076,29.95732],[80.8778,30.13384],[80.93695,30.18229],[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.22805,31.34963],[79.14016,31.43403],[79.01931,31.42817],[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]]]]}},{type:"Feature",properties:{iso1A2:"IO",iso1A3:"IOT",iso1N3:"086",wikidata:"Q43448",nameEn:"British Indian Ocean Territory",country:"GB",groups:["014","202","002"],callingCodes:["246"]},geometry:{type:"MultiPolygon",coordinates:[[[[70.64754,-4.95745],[70.67958,-8.2663],[73.70488,-4.92492],[70.64754,-4.95745]]]]}},{type:"Feature",properties:{iso1A2:"IQ",iso1A3:"IRQ",iso1N3:"368",wikidata:"Q796",nameEn:"Iraq",groups:["145","142"],callingCodes:["964"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"IR",iso1A3:"IRN",iso1N3:"364",wikidata:"Q794",nameEn:"Iran",groups:["034","142"],callingCodes:["98"]},geometry:{type:"MultiPolygon",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.5251,24.57287],[61.57592,25.0492],[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]]]]}},{type:"Feature",properties:{iso1A2:"IS",iso1A3:"ISL",iso1N3:"352",wikidata:"Q189",nameEn:"Iceland",groups:["154","150"],callingCodes:["354"]},geometry:{type:"MultiPolygon",coordinates:[[[[-33.15676,62.62995],[-8.25539,63.0423],[-15.70914,69.67442],[-33.15676,62.62995]]]]}},{type:"Feature",properties:{iso1A2:"IT",iso1A3:"ITA",iso1N3:"380",wikidata:"Q38",nameEn:"Italy",groups:["EU","039","150"],callingCodes:["39"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"JE",iso1A3:"JEY",iso1N3:"832",wikidata:"Q785",nameEn:"Jersey",country:"GB",groups:["830","154","150"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["44 01534"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.00491,48.86706],[-1.83944,49.23037],[-2.09454,49.46288],[-2.65349,49.15373],[-2.00491,48.86706]]]]}},{type:"Feature",properties:{iso1A2:"JM",iso1A3:"JAM",iso1N3:"388",wikidata:"Q766",nameEn:"Jamaica",aliases:["JA"],groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 876","1 658"]},geometry:{type:"MultiPolygon",coordinates:[[[[-75.50728,17.08879],[-76.34192,18.86145],[-78.75694,18.78765],[-78.34606,16.57862],[-75.50728,17.08879]]]]}},{type:"Feature",properties:{iso1A2:"JO",iso1A3:"JOR",iso1N3:"400",wikidata:"Q810",nameEn:"Jordan",groups:["145","142"],callingCodes:["962"]},geometry:{type:"MultiPolygon",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.88293,29.37455],[34.95987,29.35727],[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]]]]}},{type:"Feature",properties:{iso1A2:"JP",iso1A3:"JPN",iso1N3:"392",wikidata:"Q17",nameEn:"Japan",groups:["030","142"],driveSide:"left",callingCodes:["81"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"KE",iso1A3:"KEN",iso1N3:"404",wikidata:"Q114",nameEn:"Kenya",groups:["014","202","002"],driveSide:"left",callingCodes:["254"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"KG",iso1A3:"KGZ",iso1N3:"417",wikidata:"Q813",nameEn:"Kyrgyzstan",groups:["143","142"],callingCodes:["996"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"KH",iso1A3:"KHM",iso1N3:"116",wikidata:"Q424",nameEn:"Cambodia",groups:["035","142"],callingCodes:["855"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"KI",iso1A3:"KIR",iso1N3:"296",wikidata:"Q710",nameEn:"Kiribati",groups:["057","009"],driveSide:"left",callingCodes:["686"]},geometry:{type:"MultiPolygon",coordinates:[[[[169,3.9],[169,-3.5],[178,-3.5],[178,3.9],[169,3.9]]],[[[-158.62058,-1.35506],[-161.04969,-1.36251],[-175.33482,-1.40631],[-175.31804,-7.54825],[-174.18707,-7.54408],[-167.75329,-7.52784],[-156.50903,-7.4975],[-156.4957,-12.32002],[-149.61166,-12.30171],[-149.6249,-7.51261],[-149.65979,5.27712],[-161.06795,5.2462],[-161.05669,1.11722],[-158.62734,1.1296],[-158.62058,-1.35506]]]]}},{type:"Feature",properties:{iso1A2:"KM",iso1A3:"COM",iso1N3:"174",wikidata:"Q970",nameEn:"Comoros",groups:["014","202","002"],callingCodes:["269"]},geometry:{type:"MultiPolygon",coordinates:[[[[42.93552,-11.11413],[42.99868,-12.65261],[44.75722,-12.58368],[44.69407,-11.04481],[42.93552,-11.11413]]]]}},{type:"Feature",properties:{iso1A2:"KN",iso1A3:"KNA",iso1N3:"659",wikidata:"Q763",nameEn:"St. Kitts and Nevis",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 869"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.27053,17.22145],[-62.76692,17.64353],[-63.11114,17.23125],[-62.62949,16.82364],[-62.27053,17.22145]]]]}},{type:"Feature",properties:{iso1A2:"KP",iso1A3:"PRK",iso1N3:"408",wikidata:"Q423",nameEn:"North Korea",groups:["030","142"],callingCodes:["850"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"KR",iso1A3:"KOR",iso1N3:"410",wikidata:"Q884",nameEn:"South Korea",groups:["030","142"],callingCodes:["82"]},geometry:{type:"MultiPolygon",coordinates:[[[[133.61399,37.41],[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.61399,37.41]]]]}},{type:"Feature",properties:{iso1A2:"KW",iso1A3:"KWT",iso1N3:"414",wikidata:"Q817",nameEn:"Kuwait",groups:["145","142"],callingCodes:["965"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"KY",iso1A3:"CYM",iso1N3:"136",wikidata:"Q5785",nameEn:"Cayman Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 345"]},geometry:{type:"MultiPolygon",coordinates:[[[[-82.11509,19.60401],[-80.36068,18.11751],[-79.32727,20.06742],[-82.11509,19.60401]]]]}},{type:"Feature",properties:{iso1A2:"KZ",iso1A3:"KAZ",iso1N3:"398",wikidata:"Q232",nameEn:"Kazakhstan",groups:["143","142"],callingCodes:["7"]},geometry:{type:"MultiPolygon",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],[46.98795,49.23531],[47.04416,49.17152],[47.01458,49.07085],[46.91104,48.99715],[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]]]]}},{type:"Feature",properties:{iso1A2:"LA",iso1A3:"LAO",iso1N3:"418",wikidata:"Q819",nameEn:"Laos",groups:["035","142"],callingCodes:["856"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"LB",iso1A3:"LBN",iso1N3:"422",wikidata:"Q822",nameEn:"Lebanon",aliases:["RL"],groups:["145","142"],callingCodes:["961"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"LC",iso1A3:"LCA",iso1N3:"662",wikidata:"Q760",nameEn:"St. Lucia",aliases:["WL"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 758"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.5958,14.23076],[-61.26561,14.25664],[-61.43129,13.68336],[-60.70539,13.41452],[-60.5958,14.23076]]]]}},{type:"Feature",properties:{iso1A2:"LI",iso1A3:"LIE",iso1N3:"438",wikidata:"Q347",nameEn:"Liechtenstein",aliases:["FL"],groups:["155","150"],callingCodes:["423"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"LK",iso1A3:"LKA",iso1N3:"144",wikidata:"Q854",nameEn:"Sri Lanka",groups:["034","142"],driveSide:"left",callingCodes:["94"]},geometry:{type:"MultiPolygon",coordinates:[[[[76.25812,4.62435],[85.15017,5.21497],[80.48418,10.20786],[79.42124,9.80115],[79.50447,8.91876],[76.25812,4.62435]]]]}},{type:"Feature",properties:{iso1A2:"LR",iso1A3:"LBR",iso1N3:"430",wikidata:"Q1014",nameEn:"Liberia",groups:["011","202","002"],callingCodes:["231"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"LS",iso1A3:"LSO",iso1N3:"426",wikidata:"Q1013",nameEn:"Lesotho",groups:["018","202","002"],driveSide:"left",callingCodes:["266"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"LT",iso1A3:"LTU",iso1N3:"440",wikidata:"Q37",nameEn:"Lithuania",groups:["EU","154","150"],callingCodes:["370"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"LU",iso1A3:"LUX",iso1N3:"442",wikidata:"Q32",nameEn:"Luxembourg",groups:["EU","155","150"],callingCodes:["352"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"LV",iso1A3:"LVA",iso1N3:"428",wikidata:"Q211",nameEn:"Latvia",groups:["EU","154","150"],callingCodes:["371"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"LY",iso1A3:"LBY",iso1N3:"434",wikidata:"Q1016",nameEn:"Libya",groups:["015","002"],callingCodes:["218"]},geometry:{type:"MultiPolygon",coordinates:[[[[22.5213,33.45682],[11.66543,33.34642],[11.56255,33.16754],[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.83101,31.31921],[25.06041,31.57937],[25.14001,31.67534],[25.63787,31.9359],[22.5213,33.45682]]]]}},{type:"Feature",properties:{iso1A2:"MA",iso1A3:"MAR",iso1N3:"504",wikidata:"Q1028",nameEn:"Morocco",groups:["015","002"],callingCodes:["212"]},geometry:{type:"MultiPolygon",coordinates:[[[[-2.27707,35.35051],[-2.85819,35.63219],[-5.10878,36.05227],[-5.64962,35.93752],[-7.27694,35.93599],[-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.92224,35.3401],[-2.92181,35.28599],[-2.92674,35.27313],[-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.92224,35.3401]],[[-3.90602,35.21494],[-3.90288,35.22024],[-3.88617,35.21406],[-3.88926,35.20841],[-3.90602,35.21494]],[[-4.30191,35.17419],[-4.29436,35.17149],[-4.30112,35.17058],[-4.30191,35.17419]],[[-2.41312,35.17111],[-2.44887,35.17075],[-2.44896,35.18777],[-2.41265,35.1877],[-2.41312,35.17111]],[[-5.38491,35.92591],[-5.27635,35.91222],[-5.27056,35.88794],[-5.34379,35.8711],[-5.35844,35.87375],[-5.37338,35.88417],[-5.38491,35.92591]]]]}},{type:"Feature",properties:{iso1A2:"MC",iso1A3:"MCO",iso1N3:"492",wikidata:"Q235",nameEn:"Monaco",groups:["155","150"],callingCodes:["377"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"MD",iso1A3:"MDA",iso1N3:"498",wikidata:"Q217",nameEn:"Moldova",groups:["151","150"],callingCodes:["373"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"ME",iso1A3:"MNE",iso1N3:"499",wikidata:"Q236",nameEn:"Montenegro",groups:["039","150"],callingCodes:["382"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"MF",iso1A3:"MAF",iso1N3:"663",wikidata:"Q126125",nameEn:"Saint-Martin",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["590"]},geometry:{type:"MultiPolygon",coordinates:[[[[-62.93924,18.02904],[-62.75637,18.13489],[-62.86666,18.19278],[-63.35989,18.06012],[-63.33064,17.9615],[-63.13584,18.0541],[-63.11096,18.05368],[-63.09686,18.04608],[-63.07759,18.04943],[-63.0579,18.06614],[-63.04039,18.05619],[-63.02323,18.05757],[-62.93924,18.02904]]]]}},{type:"Feature",properties:{iso1A2:"MG",iso1A3:"MDG",iso1N3:"450",wikidata:"Q1019",nameEn:"Madagascar",aliases:["RM"],groups:["014","202","002"],callingCodes:["261"]},geometry:{type:"MultiPolygon",coordinates:[[[[51.94557,-12.74579],[49.10033,-10.96054],[43.72277,-16.09877],[40.40841,-23.17181],[45.90777,-29.77366],[51.94557,-12.74579]]]]}},{type:"Feature",properties:{iso1A2:"MH",iso1A3:"MHL",iso1N3:"584",wikidata:"Q709",nameEn:"Marshall Islands",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["692"]},geometry:{type:"MultiPolygon",coordinates:[[[[169,3.9],[173.53711,5.70687],[169.29099,15.77133],[159.04653,10.59067],[169,3.9]]]]}},{type:"Feature",properties:{iso1A2:"MK",iso1A3:"MKD",iso1N3:"807",wikidata:"Q221",nameEn:"North Macedonia",groups:["039","150"],callingCodes:["389"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"ML",iso1A3:"MLI",iso1N3:"466",wikidata:"Q912",nameEn:"Mali",groups:["011","202","002"],callingCodes:["223"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"MM",iso1A3:"MMR",iso1N3:"104",wikidata:"Q836",nameEn:"Myanmar",aliases:["Burma","BU"],groups:["035","142"],callingCodes:["95"]},geometry:{type:"MultiPolygon",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.37665,20.72172],[92.28464,20.63179],[92.31348,20.57137],[92.4302,20.5688],[92.39837,20.38919],[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]]]]}},{type:"Feature",properties:{iso1A2:"MN",iso1A3:"MNG",iso1N3:"496",wikidata:"Q711",nameEn:"Mongolia",groups:["030","142"],callingCodes:["976"]},geometry:{type:"MultiPolygon",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.28833,42.58524],[101.80515,42.50074],[102.07645,42.22519],[102.42826,42.15137],[102.72403,42.14675],[103.3685,41.89696],[103.92804,41.78246],[104.52258,41.8706],[104.51667,41.66113],[104.91272,41.64619],[105.01119,41.58382],[105.24708,41.7442],[106.76517,42.28741],[107.24774,42.36107],[107.29755,42.41395],[107.49681,42.46221],[107.57258,42.40898],[108.23156,42.45532],[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.63821,44.74326],[113.909,44.91444],[114.08071,44.92847],[114.5166,45.27189],[114.54801,45.38337],[114.74612,45.43585],[114.94546,45.37377],[115.35757,45.39106],[115.69688,45.45761],[115.91898,45.6227],[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.07252,46.35818],[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.30261,46.6083],[119.37306,46.61132],[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],[117.08918,47.82242],[116.87527,47.88836],[116.67405,47.89039],[116.4465,47.83662],[116.2527,47.87766],[116.08431,47.80693],[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]]]]}},{type:"Feature",properties:{iso1A2:"MO",iso1A3:"MAC",iso1N3:"446",wikidata:"Q14773",nameEn:"Macau",aliases:["Macao"],country:"CN",groups:["030","142"],driveSide:"left",callingCodes:["853"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"MP",iso1A3:"MNP",iso1N3:"580",wikidata:"Q16644",nameEn:"Northern Mariana Islands",country:"US",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["1 670"]},geometry:{type:"MultiPolygon",coordinates:[[[[143.82485,13.92273],[146.25931,13.85876],[146.6755,21.00809],[144.18594,21.03576],[143.82485,13.92273]]]]}},{type:"Feature",properties:{iso1A2:"MQ",iso1A3:"MTQ",iso1N3:"474",wikidata:"Q17054",nameEn:"Martinique",country:"FR",groups:["EU","029","003","419","019"],callingCodes:["596"]},geometry:{type:"MultiPolygon",coordinates:[[[[-60.5958,14.23076],[-60.69955,15.22234],[-61.51867,14.96709],[-61.26561,14.25664],[-60.5958,14.23076]]]]}},{type:"Feature",properties:{iso1A2:"MR",iso1A3:"MRT",iso1N3:"478",wikidata:"Q1025",nameEn:"Mauritania",groups:["011","202","002"],callingCodes:["222"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"MS",iso1A3:"MSR",iso1N3:"500",wikidata:"Q13353",nameEn:"Montserrat",country:"GB",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 664"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.83929,16.66647],[-62.14123,17.02632],[-62.52079,16.69392],[-62.17275,16.35721],[-61.83929,16.66647]]]]}},{type:"Feature",properties:{iso1A2:"MT",iso1A3:"MLT",iso1N3:"470",wikidata:"Q233",nameEn:"Malta",groups:["EU","039","150"],driveSide:"left",callingCodes:["356"]},geometry:{type:"MultiPolygon",coordinates:[[[[15.70991,35.79901],[14.07544,36.41525],[13.27636,35.20764],[15.70991,35.79901]]]]}},{type:"Feature",properties:{iso1A2:"MU",iso1A3:"MUS",iso1N3:"480",wikidata:"Q1027",nameEn:"Mauritius",groups:["014","202","002"],driveSide:"left",callingCodes:["230"]},geometry:{type:"MultiPolygon",coordinates:[[[[56.73473,-21.9174],[64.11105,-21.5783],[63.47388,-9.1938],[56.09755,-9.55401],[56.73473,-21.9174]]]]}},{type:"Feature",properties:{iso1A2:"MV",iso1A3:"MDV",iso1N3:"462",wikidata:"Q826",nameEn:"Maldives",groups:["034","142"],driveSide:"left",callingCodes:["960"]},geometry:{type:"MultiPolygon",coordinates:[[[[71.27292,7.36038],[73.37814,-3.88401],[74.6203,7.39289],[71.27292,7.36038]]]]}},{type:"Feature",properties:{iso1A2:"MW",iso1A3:"MWI",iso1N3:"454",wikidata:"Q1020",nameEn:"Malawi",groups:["014","202","002"],driveSide:"left",callingCodes:["265"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"MX",iso1A3:"MEX",iso1N3:"484",wikidata:"Q96",nameEn:"Mexico",groups:["013","003","419","019"],callingCodes:["52"]},geometry:{type:"MultiPolygon",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],[-109.05235,31.3333],[-111.07523,31.33232],[-112.34553,31.7357],[-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]]]]}},{type:"Feature",properties:{iso1A2:"MY",iso1A3:"MYS",iso1N3:"458",wikidata:"Q833",nameEn:"Malaysia",groups:["035","142"],driveSide:"left",callingCodes:["60"]},geometry:{type:"MultiPolygon",coordinates:[[[[114.08532,4.64632],[109.55486,8.10026],[104.81582,8.03101],[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],[108.10426,5.42408],[109.71058,2.32059],[109.64506,2.08014],[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],[117.89538,4.16637],[118.07935,4.15511],[118.8663,4.44172],[118.75416,4.59798],[119.44841,5.09568],[119.34756,5.53889],[117.89159,6.25755],[117.43832,7.3895],[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.08532,4.64632]]]]}},{type:"Feature",properties:{iso1A2:"MZ",iso1A3:"MOZ",iso1N3:"508",wikidata:"Q1029",nameEn:"Mozambique",groups:["014","202","002"],driveSide:"left",callingCodes:["258"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"NA",iso1A3:"NAM",iso1N3:"516",wikidata:"Q1030",nameEn:"Namibia",groups:["018","202","002"],driveSide:"left",callingCodes:["264"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"NC",iso1A3:"NCL",iso1N3:"540",wikidata:"Q33788",nameEn:"New Caledonia",country:"FR",groups:["054","009"],callingCodes:["687"]},geometry:{type:"MultiPolygon",coordinates:[[[[158.65519,-23.4036],[174.90025,-23.53966],[162.93363,-17.28904],[157.83842,-18.82563],[158.65519,-23.4036]]]]}},{type:"Feature",properties:{iso1A2:"NE",iso1A3:"NER",iso1N3:"562",wikidata:"Q1032",nameEn:"Niger",aliases:["RN"],groups:["011","202","002"],callingCodes:["227"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"NF",iso1A3:"NFK",iso1N3:"574",wikidata:"Q31057",nameEn:"Norfolk Island",country:"AU",groups:["053","009"],driveSide:"left",callingCodes:["672 3"]},geometry:{type:"MultiPolygon",coordinates:[[[[169.82316,-28.16667],[166.29505,-28.29175],[167.94076,-30.60745],[169.82316,-28.16667]]]]}},{type:"Feature",properties:{iso1A2:"NG",iso1A3:"NGA",iso1N3:"566",wikidata:"Q1033",nameEn:"Nigeria",groups:["011","202","002"],callingCodes:["234"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"NI",iso1A3:"NIC",iso1N3:"558",wikidata:"Q811",nameEn:"Nicaragua",groups:["013","003","419","019"],callingCodes:["505"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"NL",iso1A3:"NLD",iso1N3:"528",wikidata:"Q55",nameEn:"Netherlands",groups:["EU","155","150"],callingCodes:["31"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"NO",iso1A3:"NOR",iso1N3:"578",wikidata:"Q20",nameEn:"Norway",groups:["154","150"],callingCodes:["47"]},geometry:{type:"MultiPolygon",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],[18.46509,71.28681],[-0.3751,61.32236],[7.28637,57.35913],[10.40861,58.38489]]]]}},{type:"Feature",properties:{iso1A2:"NP",iso1A3:"NPL",iso1N3:"524",wikidata:"Q837",nameEn:"Nepal",groups:["034","142"],driveSide:"left",callingCodes:["977"]},geometry:{type:"MultiPolygon",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.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.93695,30.18229],[80.8778,30.13384],[80.67076,29.95732],[80.60226,29.95732],[80.56957,29.88176],[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],[80.89648,28.47237],[81.08507,28.38346],[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]]]]}},{type:"Feature",properties:{iso1A2:"NR",iso1A3:"NRU",iso1N3:"520",wikidata:"Q697",nameEn:"Nauru",groups:["057","009"],driveSide:"left",callingCodes:["674"]},geometry:{type:"MultiPolygon",coordinates:[[[[166.95155,0.14829],[166.21778,-0.7977],[167.60042,-0.88259],[166.95155,0.14829]]]]}},{type:"Feature",properties:{iso1A2:"NU",iso1A3:"NIU",iso1N3:"570",wikidata:"Q34020",nameEn:"Niue",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["683"]},geometry:{type:"MultiPolygon",coordinates:[[[[-173.13438,-14.94228],[-173.11048,-23.23027],[-167.73129,-23.22266],[-167.73854,-14.92809],[-171.14262,-14.93704],[-173.13438,-14.94228]]]]}},{type:"Feature",properties:{iso1A2:"NZ",iso1A3:"NZL",iso1N3:"554",wikidata:"Q664",nameEn:"New Zealand",groups:["053","009"],driveSide:"left",callingCodes:["64"]},geometry:{type:"MultiPolygon",coordinates:[[[[-180,-24.21376],[-179.93224,-45.18423],[-155.99562,-45.16785],[-180,-24.21376]]],[[[161.96603,-56.07661],[179.49541,-50.04657],[179.49541,-36.79303],[169.6687,-29.09191],[161.96603,-56.07661]]]]}},{type:"Feature",properties:{iso1A2:"OM",iso1A3:"OMN",iso1N3:"512",wikidata:"Q842",nameEn:"Oman",groups:["145","142"],callingCodes:["968"]},geometry:{type:"MultiPolygon",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],[53.09917,16.67084],[53.32998,16.16312],[56.66759,17.24021],[61.45114,22.55394]]]]}},{type:"Feature",properties:{iso1A2:"PA",iso1A3:"PAN",iso1N3:"591",wikidata:"Q804",nameEn:"Panama",groups:["013","003","419","019"],callingCodes:["507"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"PE",iso1A3:"PER",iso1N3:"604",wikidata:"Q419",nameEn:"Peru",groups:["005","419","019"],callingCodes:["51"]},geometry:{type:"MultiPolygon",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.24475,-3.47846],[-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]]]]}},{type:"Feature",properties:{iso1A2:"PF",iso1A3:"PYF",iso1N3:"258",wikidata:"Q30971",nameEn:"French Polynesia",country:"FR",groups:["061","009"],callingCodes:["689"]},geometry:{type:"MultiPolygon",coordinates:[[[[-149.6249,-7.51261],[-149.61166,-12.30171],[-156.4957,-12.32002],[-156.46451,-23.21255],[-156.44843,-28.52556],[-133.59543,-28.4709],[-133.61511,-21.93325],[-133.65593,-7.46952],[-149.6249,-7.51261]]]]}},{type:"Feature",properties:{iso1A2:"PG",iso1A3:"PNG",iso1N3:"598",wikidata:"Q691",nameEn:"Papua New Guinea",groups:["054","009"],driveSide:"left",callingCodes:["675"]},geometry:{type:"MultiPolygon",coordinates:[[[[141.03157,2.12829],[140.99813,-6.3233],[140.85295,-6.72996],[141.01763,-6.90181],[141.00782,-9.1242],[140.88922,-9.34945],[142.0601,-9.56571],[142.0953,-9.23534],[142.1462,-9.19923],[142.23304,-9.19253],[142.31447,-9.24611],[142.5723,-9.35994],[142.81927,-9.31709],[144.30183,-9.48146],[155.22803,-12.9001],[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]]]]}},{type:"Feature",properties:{iso1A2:"PH",iso1A3:"PHL",iso1N3:"608",wikidata:"Q928",nameEn:"Philippines",aliases:["PI","RP"],groups:["035","142"],callingCodes:["63"]},geometry:{type:"MultiPolygon",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.43832,7.3895],[117.89159,6.25755],[119.34756,5.53889],[119.44841,5.09568],[118.75416,4.59798],[118.8663,4.44172],[118.07935,4.15511],[118.41402,3.99509],[124.97752,4.82064],[129.19694,7.84182]]]]}},{type:"Feature",properties:{iso1A2:"PK",iso1A3:"PAK",iso1N3:"586",wikidata:"Q843",nameEn:"Pakistan",groups:["034","142"],driveSide:"left",callingCodes:["92"]},geometry:{type:"MultiPolygon",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.57592,25.0492],[61.5251,24.57287],[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]]]]}},{type:"Feature",properties:{iso1A2:"PL",iso1A3:"POL",iso1N3:"616",wikidata:"Q36",nameEn:"Poland",groups:["EU","151","150"],callingCodes:["48"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"PM",iso1A3:"SPM",iso1N3:"666",wikidata:"Q34617",nameEn:"Saint Pierre and Miquelon",country:"FR",groups:["021","003","019"],callingCodes:["508"]},geometry:{type:"MultiPolygon",coordinates:[[[[-56.72993,46.65575],[-55.90758,46.6223],[-56.27503,47.39728],[-56.72993,46.65575]]]]}},{type:"Feature",properties:{iso1A2:"PN",iso1A3:"PCN",iso1N3:"612",wikidata:"Q35672",nameEn:"Pitcairn Islands",country:"GB",groups:["061","009"],driveSide:"left",callingCodes:["64"]},geometry:{type:"MultiPolygon",coordinates:[[[[-133.59543,-28.4709],[-122.0366,-24.55017],[-133.61511,-21.93325],[-133.59543,-28.4709]]]]}},{type:"Feature",properties:{iso1A2:"PR",iso1A3:"PRI",iso1N3:"630",wikidata:"Q1183",nameEn:"Puerto Rico",country:"US",groups:["029","003","419","019"],roadSpeedUnit:"mph",callingCodes:["1 787","1 939"]},geometry:{type:"MultiPolygon",coordinates:[[[[-65.27974,17.56928],[-65.02435,18.73231],[-67.99519,18.97186],[-68.20301,17.83927],[-65.27974,17.56928]]]]}},{type:"Feature",properties:{iso1A2:"PS",iso1A3:"PSE",iso1N3:"275",wikidata:"Q23792",nameEn:"Palestine",country:"IL",groups:["145","142"],callingCodes:["970"]},geometry:{type:"MultiPolygon",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]]],[[[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]]]]}},{type:"Feature",properties:{iso1A2:"PT",iso1A3:"PRT",iso1N3:"620",wikidata:"Q45",nameEn:"Portugal",groups:["EU","039","150"],callingCodes:["351"]},geometry:{type:"MultiPolygon",coordinates:[[[[-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],[-9.14112,41.86623],[-36.43765,41.39418],[-15.92339,29.50503],[-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]]]]}},{type:"Feature",properties:{iso1A2:"PW",iso1A3:"PLW",iso1N3:"585",wikidata:"Q695",nameEn:"Palau",groups:["057","009"],roadSpeedUnit:"mph",callingCodes:["680"]},geometry:{type:"MultiPolygon",coordinates:[[[[128.97621,3.08804],[134.40878,1.79674],[136.27107,6.73747],[136.04605,12.45908],[128.97621,3.08804]]]]}},{type:"Feature",properties:{iso1A2:"PY",iso1A3:"PRY",iso1N3:"600",wikidata:"Q733",nameEn:"Paraguay",groups:["005","419","019"],callingCodes:["595"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"QA",iso1A3:"QAT",iso1N3:"634",wikidata:"Q846",nameEn:"Qatar",groups:["145","142"],callingCodes:["974"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"RE",iso1A3:"REU",iso1N3:"638",wikidata:"Q17070",nameEn:"Réunion",country:"FR",groups:["EU","014","202","002"],callingCodes:["262"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.37984,-21.23941],[56.73473,-21.9174],[56.62373,-20.2711],[53.37984,-21.23941]]]]}},{type:"Feature",properties:{iso1A2:"RO",iso1A3:"ROU",iso1N3:"642",wikidata:"Q218",nameEn:"Romania",groups:["EU","151","150"],callingCodes:["40"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"RS",iso1A3:"SRB",iso1N3:"688",wikidata:"Q403",nameEn:"Serbia",groups:["039","150"],callingCodes:["381"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"RU",iso1A3:"RUS",iso1N3:"643",wikidata:"Q159",nameEn:"Russia",groups:["151","150"],callingCodes:["7"]},geometry:{type:"MultiPolygon",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]]],[[[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],[46.91104,48.99715],[47.01458,49.07085],[47.04416,49.17152],[46.98795,49.23531],[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],[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.07142,49.68482],[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],[36.48095,82.16765],[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],[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],[34.96015,45.75634],[34.79905,45.81009],[34.80153,45.90047],[34.75479,45.90705],[34.66679,45.97136],[34.60861,45.99347],[34.55889,45.99347],[34.52011,45.95097],[34.48729,45.94267],[34.44155,45.95995],[34.41221,46.00245],[34.33912,46.06114],[34.25111,46.0532],[34.181,46.06804],[34.12929,46.10494],[34.07311,46.11769],[34.05272,46.10838],[33.91549,46.15938],[33.85234,46.19863],[33.79715,46.20482],[33.74047,46.18555],[33.646,46.23028],[33.61517,46.22615],[33.63854,46.14147],[33.61467,46.13561],[33.57318,46.10317],[33.59087,46.06013],[33.54017,46.0123],[31.62627,45.50633],[32.99857,44.48323],[33.66142,43.9825],[39.81147,43.06294]]],[[[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],[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]]]]}},{type:"Feature",properties:{iso1A2:"RW",iso1A3:"RWA",iso1N3:"646",wikidata:"Q1037",nameEn:"Rwanda",groups:["014","202","002"],callingCodes:["250"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SA",iso1A3:"SAU",iso1N3:"682",wikidata:"Q851",nameEn:"Saudi Arabia",groups:["145","142"],callingCodes:["966"]},geometry:{type:"MultiPolygon",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.95987,29.35727],[34.88293,29.37455],[34.46254,27.99552],[34.51305,27.70027],[37.8565,22.00903],[39.63762,18.37348],[41.37609,16.19728],[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]]]]}},{type:"Feature",properties:{iso1A2:"SB",iso1A3:"SLB",iso1N3:"090",wikidata:"Q685",nameEn:"Solomon Islands",groups:["054","009"],driveSide:"left",callingCodes:["677"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-12.72535],[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],[160.04026,-13.08769],[174,-12.72535]]]]}},{type:"Feature",properties:{iso1A2:"SC",iso1A3:"SYC",iso1N3:"690",wikidata:"Q1042",nameEn:"Seychelles",groups:["014","202","002"],driveSide:"left",callingCodes:["248"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.75112,-10.38913],[54.83239,-10.93575],[66.3222,5.65313],[43.75112,-10.38913]]]]}},{type:"Feature",properties:{iso1A2:"SD",iso1A3:"SDN",iso1N3:"729",wikidata:"Q1049",nameEn:"Sudan",groups:["015","002"],callingCodes:["249"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SE",iso1A3:"SWE",iso1N3:"752",wikidata:"Q34",nameEn:"Sweden",groups:["EU","154","150"],callingCodes:["46"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SG",iso1A3:"SGP",iso1N3:"702",wikidata:"Q334",nameEn:"Singapore",groups:["035","142"],driveSide:"left",callingCodes:["65"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SH",iso1A3:"SHN",iso1N3:"654",wikidata:"Q34497",nameEn:"Saint Helena, Ascension and Tristan da Cunha",country:"GB",groups:["011","202","002"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["290"]},geometry:{type:"MultiPolygon",coordinates:[[[[-14.82771,-8.70814],[-13.48367,-36.6746],[-11.55782,-36.60319],[-11.48092,-37.8367],[-13.41694,-37.88844],[-13.29655,-40.02846],[-9.34669,-41.00353],[-4.97086,-15.55882],[-13.33271,-8.07391],[-14.82771,-8.70814]]]]}},{type:"Feature",properties:{iso1A2:"SI",iso1A3:"SVN",iso1N3:"705",wikidata:"Q215",nameEn:"Slovenia",groups:["EU","039","150"],callingCodes:["386"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SJ",iso1A3:"SJM",iso1N3:"744",wikidata:"Q842829",nameEn:"Svalbard and Jan Mayen",country:"NO",groups:["154","150"],callingCodes:["47 79"]},geometry:{type:"MultiPolygon",coordinates:[[[[-7.49892,77.24208],[32.07813,72.01005],[36.85549,84.09565],[-7.49892,77.24208]]],[[[-9.18243,72.23144],[-10.71459,70.09565],[-5.93364,70.76368],[-9.18243,72.23144]]]]}},{type:"Feature",properties:{iso1A2:"SK",iso1A3:"SVK",iso1N3:"703",wikidata:"Q214",nameEn:"Slovakia",groups:["EU","151","150"],callingCodes:["421"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SL",iso1A3:"SLE",iso1N3:"694",wikidata:"Q1044",nameEn:"Sierra Leone",groups:["011","202","002"],callingCodes:["232"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SM",iso1A3:"SMR",iso1N3:"674",wikidata:"Q238",nameEn:"San Marino",groups:["039","150"],callingCodes:["378"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SN",iso1A3:"SEN",iso1N3:"686",wikidata:"Q1041",nameEn:"Senegal",groups:["011","202","002"],callingCodes:["221"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SO",iso1A3:"SOM",iso1N3:"706",wikidata:"Q1045",nameEn:"Somalia",groups:["014","202","002"],callingCodes:["252"]},geometry:{type:"MultiPolygon",coordinates:[[[[48.95249,11.56816],[43.42425,11.70983],[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],[49.16337,2.78611],[52.253,11.68582],[51.12877,12.56479],[48.95249,11.56816]]]]}},{type:"Feature",properties:{iso1A2:"SR",iso1A3:"SUR",iso1N3:"740",wikidata:"Q730",nameEn:"Suriname",groups:["005","419","019"],driveSide:"left",callingCodes:["597"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SS",iso1A3:"SSD",iso1N3:"728",wikidata:"Q958",nameEn:"South Sudan",groups:["014","202","002"],callingCodes:["211"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"ST",iso1A3:"STP",iso1N3:"678",wikidata:"Q1039",nameEn:"São Tomé and Principe",groups:["017","202","002"],callingCodes:["239"]},geometry:{type:"MultiPolygon",coordinates:[[[[5.9107,-0.09539],[6.69416,-0.53945],[8.0168,1.79377],[7.23334,2.23756],[5.9107,-0.09539]]]]}},{type:"Feature",properties:{iso1A2:"SV",iso1A3:"SLV",iso1N3:"222",wikidata:"Q792",nameEn:"El Salvador",groups:["013","003","419","019"],callingCodes:["503"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SX",iso1A3:"SXM",iso1N3:"534",wikidata:"Q26273",nameEn:"Sint Maarten",country:"NL",groups:["029","003","419","019"],callingCodes:["1 721"]},geometry:{type:"MultiPolygon",coordinates:[[[[-63.29212,17.90532],[-63.07669,17.79659],[-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.11096,18.05368],[-63.13584,18.0541],[-63.33064,17.9615],[-63.29212,17.90532]]]]}},{type:"Feature",properties:{iso1A2:"SY",iso1A3:"SYR",iso1N3:"760",wikidata:"Q858",nameEn:"Syria",groups:["145","142"],callingCodes:["963"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"SZ",iso1A3:"SWZ",iso1N3:"748",wikidata:"Q1050",nameEn:"Eswatini",aliases:["Swaziland"],groups:["018","202","002"],driveSide:"left",callingCodes:["268"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"TA",iso1A3:"TAA",wikidata:"Q220982",nameEn:"Tristan da Cunha",country:"GB",groups:["SH","011","202","002"],isoStatus:"excRes",driveSide:"left",roadSpeedUnit:"mph",callingCodes:["290 8","44 20"]},geometry:{type:"MultiPolygon",coordinates:[[[[-13.48367,-36.6746],[-13.41694,-37.88844],[-11.48092,-37.8367],[-11.55782,-36.60319],[-13.48367,-36.6746]]]]}},{type:"Feature",properties:{iso1A2:"TC",iso1A3:"TCA",iso1N3:"796",wikidata:"Q18221",nameEn:"Turks and Caicos Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 649"]},geometry:{type:"MultiPolygon",coordinates:[[[[-72.41726,22.40371],[-72.72017,21.48055],[-71.46138,20.64433],[-70.63262,21.53631],[-72.41726,22.40371]]]]}},{type:"Feature",properties:{iso1A2:"TD",iso1A3:"TCD",iso1N3:"148",wikidata:"Q657",nameEn:"Chad",groups:["017","202","002"],callingCodes:["235"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"TF",iso1A3:"ATF",iso1N3:"260",wikidata:"Q129003",nameEn:"French Southern and Antarctic Lands",country:"FR",groups:["014","202","002"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.53458,-16.36909],[54.96649,-16.28353],[54.61476,-15.02273],[53.53458,-16.36909]]],[[[39.10324,-21.48967],[40.40841,-23.17181],[43.72277,-16.09877],[41.06663,-17.08802],[39.10324,-21.48967]]],[[[46.52682,-10.83678],[47.29063,-12.45583],[48.86266,-10.8109],[46.52682,-10.83678]]],[[[80.15867,-36.04977],[46.31615,-46.28749],[70.67507,-51.14192],[80.15867,-36.04977]]]]}},{type:"Feature",properties:{iso1A2:"TG",iso1A3:"TGO",iso1N3:"768",wikidata:"Q945",nameEn:"Togo",groups:["011","202","002"],callingCodes:["228"]},geometry:{type:"MultiPolygon",coordinates:[[[[0.50388,11.01011],[-0.13493,11.14075],[-0.14462,11.10811],[-0.05733,11.08628],[-0.0275,11.11202],[-0.00514,11.10763],[0.00342,11.08317],[0.02395,11.06229],[0.03355,10.9807],[-0.0063,10.96417],[-0.00908,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]]]]}},{type:"Feature",properties:{iso1A2:"TH",iso1A3:"THA",iso1N3:"764",wikidata:"Q869",nameEn:"Thailand",groups:["035","142"],driveSide:"left",callingCodes:["66"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"TJ",iso1A3:"TJK",iso1N3:"762",wikidata:"Q863",nameEn:"Tajikistan",groups:["143","142"],callingCodes:["992"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"TK",iso1A3:"TKL",iso1N3:"772",wikidata:"Q36823",nameEn:"Tokelau",country:"NZ",groups:["061","009"],driveSide:"left",callingCodes:["690"]},geometry:{type:"MultiPolygon",coordinates:[[[[-167.75195,-10.12005],[-167.75329,-7.52784],[-174.18707,-7.54408],[-174.17993,-10.13616],[-167.75195,-10.12005]]]]}},{type:"Feature",properties:{iso1A2:"TL",iso1A3:"TLS",iso1N3:"626",wikidata:"Q574",nameEn:"East Timor",aliases:["Timor-Leste","TP"],groups:["035","142"],driveSide:"left",callingCodes:["670"]},geometry:{type:"MultiPolygon",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.65946,-8.06136],[125.31127,-8.22976],[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]]]]}},{type:"Feature",properties:{iso1A2:"TM",iso1A3:"TKM",iso1N3:"795",wikidata:"Q874",nameEn:"Turkmenistan",groups:["143","142"],callingCodes:["993"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"TN",iso1A3:"TUN",iso1N3:"788",wikidata:"Q948",nameEn:"Tunisia",groups:["015","002"],callingCodes:["216"]},geometry:{type:"MultiPolygon",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.56255,33.16754],[11.66543,33.34642],[11.2718,37.6713]]]]}},{type:"Feature",properties:{iso1A2:"TO",iso1A3:"TON",iso1N3:"776",wikidata:"Q678",nameEn:"Tonga",groups:["061","009"],driveSide:"left",callingCodes:["676"]},geometry:{type:"MultiPolygon",coordinates:[[[[-176.74538,-22.89767],[-180,-22.90585],[-180,-24.21376],[-173.10761,-24.19665],[-173.11048,-23.23027],[-173.13438,-14.94228],[-174.17905,-14.94502],[-176.76826,-14.95183],[-176.74538,-22.89767]]]]}},{type:"Feature",properties:{iso1A2:"TR",iso1A3:"TUR",iso1N3:"792",wikidata:"Q43",nameEn:"Turkey",groups:["145","142"],callingCodes:["90"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"TT",iso1A3:"TTO",iso1N3:"780",wikidata:"Q754",nameEn:"Trinidad and Tobago",groups:["029","003","419","019"],driveSide:"left",callingCodes:["1 868"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.62505,11.18974],[-62.08693,10.04435],[-60.89962,9.81445],[-60.07172,11.77667],[-61.62505,11.18974]]]]}},{type:"Feature",properties:{iso1A2:"TV",iso1A3:"TUV",iso1N3:"798",wikidata:"Q672",nameEn:"Tuvalu",groups:["061","009"],driveSide:"left",callingCodes:["688"]},geometry:{type:"MultiPolygon",coordinates:[[[[174,-5],[174,-11.5],[179.99999,-11.5],[179.99999,-5],[174,-5]]]]}},{type:"Feature",properties:{iso1A2:"TW",iso1A3:"TWN",iso1N3:"158",wikidata:"Q865",nameEn:"Taiwan",groups:["030","142"],callingCodes:["886"]},geometry:{type:"MultiPolygon",coordinates:[[[[123.0791,22.07818],[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],[123.0791,22.07818]]]]}},{type:"Feature",properties:{iso1A2:"TZ",iso1A3:"TZA",iso1N3:"834",wikidata:"Q924",nameEn:"Tanzania",groups:["014","202","002"],driveSide:"left",callingCodes:["255"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"UA",iso1A3:"UKR",iso1N3:"804",wikidata:"Q212",nameEn:"Ukraine",groups:["151","150"],callingCodes:["380"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"UG",iso1A3:"UGA",iso1N3:"800",wikidata:"Q1036",nameEn:"Uganda",groups:["014","202","002"],driveSide:"left",callingCodes:["256"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"UM",iso1A3:"UMI",iso1N3:"581",wikidata:"Q16645",nameEn:"United States Minor Outlying Islands",country:"US",groups:["057","009"]},geometry:{type:"MultiPolygon",coordinates:[[[[-175.33482,-1.40631],[-175.33167,1.67574],[-177.43928,1.65656],[-177.43039,-1.43294],[-175.33482,-1.40631]]],[[[-161.04969,-1.36251],[-158.62058,-1.35506],[-158.62734,1.1296],[-161.05669,1.11722],[-161.04969,-1.36251]]],[[[-161.06795,5.2462],[-161.0731,7.1291],[-163.24994,7.12322],[-163.24478,5.24198],[-161.06795,5.2462]]],[[[-170.65691,16.57199],[-168.87689,16.01159],[-169.2329,17.4933],[-170.65691,16.57199]]],[[[-176.29741,29.09786],[-177.77531,29.29793],[-177.5224,27.7635],[-176.29741,29.09786]]],[[[-74.7289,18.71009],[-75.71816,18.46438],[-74.76465,18.06252],[-74.7289,18.71009]]],[[[167.34779,18.97692],[166.67967,20.14834],[165.82549,18.97692],[167.34779,18.97692]]]]}},{type:"Feature",properties:{iso1A2:"US",iso1A3:"USA",iso1N3:"840",wikidata:"Q30",nameEn:"United States of America",groups:["021","003","019"],roadSpeedUnit:"mph",callingCodes:["1"]},geometry:{type:"MultiPolygon",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]]],[[[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]]],[[[-97.13927,25.96583],[-96.92418,25.97377],[-82.02215,24.23074],[-79.89631,24.6597],[-79.14818,27.83105],[-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],[-97.24024,48.99952],[-101.36198,48.99935],[-104.05004,48.99925],[-110.0051,48.99901],[-114.0683,48.99885],[-116.04938,48.99999],[-117.03266,49.00056],[-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],[-112.34553,31.7357],[-111.07523,31.33232],[-109.05235,31.3333],[-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]]]]}},{type:"Feature",properties:{iso1A2:"UY",iso1A3:"URY",iso1N3:"858",wikidata:"Q77",nameEn:"Uruguay",groups:["005","419","019"],callingCodes:["598"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"UZ",iso1A3:"UZB",iso1N3:"860",wikidata:"Q265",nameEn:"Uzbekistan",groups:["143","142"],callingCodes:["998"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"VA",iso1A3:"VAT",iso1N3:"336",wikidata:"Q237",nameEn:"Vatican City",aliases:["Holy See"],groups:["039","150"],callingCodes:["379","39 06"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"VC",iso1A3:"VCT",iso1N3:"670",wikidata:"Q757",nameEn:"St. Vincent and the Grenadines",aliases:["WV"],groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 784"]},geometry:{type:"MultiPolygon",coordinates:[[[[-61.73897,12.61191],[-61.38256,12.52991],[-61.13395,12.51526],[-60.70539,13.41452],[-61.43129,13.68336],[-61.73897,12.61191]]]]}},{type:"Feature",properties:{iso1A2:"VE",iso1A3:"VEN",iso1N3:"862",wikidata:"Q717",nameEn:"Venezuela",aliases:["YV"],groups:["005","419","019"],callingCodes:["58"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"VG",iso1A3:"VGB",iso1N3:"092",wikidata:"Q25305",nameEn:"British Virgin Islands",country:"GB",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 284"]},geometry:{type:"MultiPolygon",coordinates:[[[[-64.03057,18.08241],[-63.75633,19.39745],[-65.02435,18.73231],[-64.86027,18.39056],[-64.64067,18.36478],[-64.646,18.10286],[-64.03057,18.08241]]]]}},{type:"Feature",properties:{iso1A2:"VI",iso1A3:"VIR",iso1N3:"850",wikidata:"Q11703",nameEn:"United States Virgin Islands",country:"US",groups:["029","003","419","019"],driveSide:"left",roadSpeedUnit:"mph",callingCodes:["1 340"]},geometry:{type:"MultiPolygon",coordinates:[[[[-65.02435,18.73231],[-65.27974,17.56928],[-64.35558,17.48384],[-64.646,18.10286],[-64.64067,18.36478],[-64.86027,18.39056],[-65.02435,18.73231]]]]}},{type:"Feature",properties:{iso1A2:"VN",iso1A3:"VNM",iso1N3:"704",wikidata:"Q881",nameEn:"Vietnam",groups:["035","142"],callingCodes:["84"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"VU",iso1A3:"VUT",iso1N3:"548",wikidata:"Q686",nameEn:"Vanuatu",groups:["054","009"],callingCodes:["678"]},geometry:{type:"MultiPolygon",coordinates:[[[[162.93363,-17.28904],[173.26254,-22.69968],[168.21179,-12.88558],[166.02864,-12.9396],[162.93363,-17.28904]]]]}},{type:"Feature",properties:{iso1A2:"WF",iso1A3:"WLF",iso1N3:"876",wikidata:"Q35555",nameEn:"Wallis and Futuna",country:"FR",groups:["061","009"],callingCodes:["681"]},geometry:{type:"MultiPolygon",coordinates:[[[[-178.60161,-14.95666],[-176.76826,-14.95183],[-174.17905,-14.94502],[-174.18596,-12.48057],[-178.60852,-12.49232],[-178.60161,-14.95666]]]]}},{type:"Feature",properties:{iso1A2:"WS",iso1A3:"WSM",iso1N3:"882",wikidata:"Q683",nameEn:"Samoa",groups:["061","009"],driveSide:"left",callingCodes:["685"]},geometry:{type:"MultiPolygon",coordinates:[[[[-174.17905,-14.94502],[-173.13438,-14.94228],[-171.14262,-14.93704],[-171.14953,-12.4725],[-174.18596,-12.48057],[-174.17905,-14.94502]]]]}},{type:"Feature",properties:{iso1A2:"XK",iso1A3:"XKX",wikidata:"Q1246",nameEn:"Kosovo",aliases:["KV"],groups:["039","150"],isoStatus:"usrAssn",callingCodes:["383"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"YE",iso1A3:"YEM",iso1N3:"887",wikidata:"Q805",nameEn:"Yemen",groups:["145","142"],callingCodes:["967"]},geometry:{type:"MultiPolygon",coordinates:[[[[53.32998,16.16312],[53.09917,16.67084],[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],[41.37609,16.19728],[41.29956,15.565],[42.63806,13.58268],[43.29075,12.79154],[43.32909,12.59711],[43.90659,12.3823],[50.51849,13.0483],[51.12877,12.56479],[52.253,11.68582],[55.69862,12.12478],[53.32998,16.16312]]]]}},{type:"Feature",properties:{iso1A2:"YT",iso1A3:"MYT",iso1N3:"175",wikidata:"Q17063",nameEn:"Mayotte",country:"FR",groups:["EU","014","202","002"],callingCodes:["262"]},geometry:{type:"MultiPolygon",coordinates:[[[[43.83794,-13.66915],[45.54824,-13.22353],[45.50237,-11.90315],[43.83794,-13.66915]]]]}},{type:"Feature",properties:{iso1A2:"ZA",iso1A3:"ZAF",iso1N3:"710",wikidata:"Q258",nameEn:"South Africa",groups:["018","202","002"],driveSide:"left",callingCodes:["27"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"ZM",iso1A3:"ZMB",iso1N3:"894",wikidata:"Q953",nameEn:"Zambia",groups:["014","202","002"],driveSide:"left",callingCodes:["260"]},geometry:{type:"MultiPolygon",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]]]]}},{type:"Feature",properties:{iso1A2:"ZW",iso1A3:"ZWE",iso1N3:"716",wikidata:"Q954",nameEn:"Zimbabwe",groups:["014","202","002"],driveSide:"left",callingCodes:["263"]},geometry:{type:"MultiPolygon",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]]]]}}];
62963 var borders = rawBorders;
62964 var whichPolygonGetter = {};
62965 var featuresByCode = {};
62966 var idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
62967 var levels = ['subterritory', 'territory', 'country', 'intermediateRegion', 'subregion', 'region', 'union', 'world'];
62968 loadDerivedDataAndCaches(borders);
62970 function loadDerivedDataAndCaches(borders) {
62971 var identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
62972 var geometryFeatures = [];
62974 for (var i in borders.features) {
62975 var _feature = borders.features[i];
62976 _feature.properties.id = _feature.properties.iso1A2 || _feature.properties.m49;
62978 loadIsoStatus(_feature);
62979 loadLevel(_feature);
62980 loadGroups(_feature);
62981 loadRoadSpeedUnit(_feature);
62982 loadDriveSide(_feature);
62983 loadFlag(_feature);
62984 cacheFeatureByIDs(_feature);
62985 if (_feature.geometry) geometryFeatures.push(_feature);
62988 for (var _i in borders.features) {
62989 var _feature2 = borders.features[_i];
62991 _feature2.properties.groups.sort(function (groupID1, groupID2) {
62992 return levels.indexOf(featuresByCode[groupID1].properties.level) - levels.indexOf(featuresByCode[groupID2].properties.level);
62995 loadMembersForGroupsOf(_feature2);
62998 var geometryOnlyCollection = {
62999 type: 'RegionFeatureCollection',
63000 features: geometryFeatures
63002 whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
63004 function loadGroups(feature) {
63005 var props = feature.properties;
63007 if (!props.groups) {
63011 if (props.country) {
63012 props.groups.push(props.country);
63015 if (props.m49 !== '001') {
63016 props.groups.push('001');
63020 function loadM49(feature) {
63021 var props = feature.properties;
63023 if (!props.m49 && props.iso1N3) {
63024 props.m49 = props.iso1N3;
63028 function loadIsoStatus(feature) {
63029 var props = feature.properties;
63031 if (!props.isoStatus && props.iso1A2) {
63032 props.isoStatus = 'official';
63036 function loadLevel(feature) {
63037 var props = feature.properties;
63038 if (props.level) return;
63040 if (!props.country) {
63041 props.level = 'country';
63042 } else if (props.isoStatus === 'official') {
63043 props.level = 'territory';
63045 props.level = 'subterritory';
63049 function loadRoadSpeedUnit(feature) {
63050 var props = feature.properties;
63052 if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
63053 props.roadSpeedUnit = 'km/h';
63057 function loadDriveSide(feature) {
63058 var props = feature.properties;
63060 if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
63061 props.driveSide = 'right';
63065 function loadFlag(feature) {
63066 if (!feature.properties.iso1A2) return;
63067 var flag = feature.properties.iso1A2.replace(/./g, function (_char) {
63068 return String.fromCodePoint(_char.charCodeAt(0) + 127397);
63070 feature.properties.emojiFlag = flag;
63073 function loadMembersForGroupsOf(feature) {
63074 var featureID = feature.properties.id;
63075 var standardizedGroupIDs = [];
63077 for (var j in feature.properties.groups) {
63078 var groupID = feature.properties.groups[j];
63079 var groupFeature = featuresByCode[groupID];
63080 standardizedGroupIDs.push(groupFeature.properties.id);
63082 if (groupFeature.properties.members) {
63083 groupFeature.properties.members.push(featureID);
63085 groupFeature.properties.members = [featureID];
63089 feature.properties.groups = standardizedGroupIDs;
63092 function cacheFeatureByIDs(feature) {
63093 for (var k in identifierProps) {
63094 var prop = identifierProps[k];
63095 var id = prop && feature.properties[prop];
63098 id = id.replace(idFilterRegex, '').toUpperCase();
63099 featuresByCode[id] = feature;
63103 if (feature.properties.aliases) {
63104 for (var j in feature.properties.aliases) {
63105 var alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
63106 featuresByCode[alias] = feature;
63112 function locArray(loc) {
63113 if (Array.isArray(loc)) {
63115 } else if (loc.coordinates) {
63116 return loc.coordinates;
63119 return loc.geometry.coordinates;
63122 function smallestFeature(loc) {
63123 var query = locArray(loc);
63124 var featureProperties = whichPolygonGetter(query);
63125 if (!featureProperties) return null;
63126 return featuresByCode[featureProperties.id];
63129 function countryFeature(loc) {
63130 var feature = smallestFeature(loc);
63131 if (!feature) return null;
63132 var countryCode = feature.properties.country || feature.properties.iso1A2;
63133 return featuresByCode[countryCode];
63136 function featureForLoc(loc, opts) {
63137 if (opts && opts.level && opts.level !== 'country') {
63138 var features = featuresContaining(loc);
63139 var targetLevel = opts.level;
63140 var targetLevelIndex = levels.indexOf(targetLevel);
63141 if (targetLevelIndex === -1) return null;
63143 for (var i in features) {
63144 var _feature3 = features[i];
63146 if (_feature3.properties.level === targetLevel || levels.indexOf(_feature3.properties.level) > targetLevelIndex) {
63154 return countryFeature(loc);
63157 function featureForID(id) {
63160 if (typeof id === 'number') {
63161 stringID = id.toString();
63163 if (stringID.length === 1) {
63164 stringID = '00' + stringID;
63165 } else if (stringID.length === 2) {
63166 stringID = '0' + stringID;
63169 stringID = id.replace(idFilterRegex, '').toUpperCase();
63172 return featuresByCode[stringID] || null;
63175 function smallestOrMatchingFeature(query) {
63176 if (_typeof(query) === 'object') {
63177 return smallestFeature(query);
63180 return featureForID(query);
63183 function feature(query, opts) {
63184 if (_typeof(query) === 'object') {
63185 return featureForLoc(query, opts);
63188 return featureForID(query);
63190 function iso1A2Code(query, opts) {
63191 var match = feature(query, opts);
63192 if (!match) return null;
63193 return match.properties.iso1A2 || null;
63195 function featuresContaining(query, strict) {
63196 var feature = smallestOrMatchingFeature(query);
63197 if (!feature) return [];
63200 if (!strict || _typeof(query) === 'object') {
63201 features.push(feature);
63204 var properties = feature.properties;
63206 for (var i in properties.groups) {
63207 var groupID = properties.groups[i];
63208 features.push(featuresByCode[groupID]);
63213 function roadSpeedUnit(query) {
63214 var feature = smallestOrMatchingFeature(query);
63215 return feature && feature.properties.roadSpeedUnit || null;
63218 var _dataDeprecated;
63222 function validationOutdatedTags() {
63223 var type = 'outdated_tags';
63224 var nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office']; // A concern here in switching to async data means that `_dataDeprecated`
63225 // and `_nsi` will not be available at first, so the data on early tiles
63226 // may not have tags validated fully.
63227 // initialize deprecated tags array
63229 _mainFileFetcher.get('deprecated').then(function (d) {
63230 return _dataDeprecated = d;
63231 })["catch"](function () {
63234 _mainFileFetcher.get('nsi_brands').then(function (d) {
63237 matcher: matcher$1(),
63240 }; // initialize name-suggestion-index matcher
63242 _nsi.matcher.buildMatchIndex(d.brands); // index all known wikipedia and wikidata tags
63245 Object.keys(d.brands).forEach(function (kvnd) {
63246 var brand = d.brands[kvnd];
63247 var wd = brand.tags['brand:wikidata'];
63248 var wp = brand.tags['brand:wikipedia'];
63251 _nsi.wikidata[wd] = kvnd;
63255 _nsi.wikipedia[wp] = kvnd;
63259 })["catch"](function () {
63263 function oldTagIssues(entity, graph) {
63264 var oldTags = Object.assign({}, entity.tags); // shallow copy
63266 var preset = _mainPresetIndex.match(entity, graph);
63267 var subtype = 'deprecated_tags';
63268 if (!preset) return []; // upgrade preset..
63270 if (preset.replacement) {
63271 var newPreset = _mainPresetIndex.item(preset.replacement);
63272 graph = actionChangePreset(entity.id, preset, newPreset, true
63273 /* skip field defaults */
63275 entity = graph.entity(entity.id);
63276 preset = newPreset;
63277 } // upgrade tags..
63280 if (_dataDeprecated) {
63281 var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
63283 if (deprecatedTags.length) {
63284 deprecatedTags.forEach(function (tag) {
63285 graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
63287 entity = graph.entity(entity.id);
63289 } // add missing addTags..
63292 var newTags = Object.assign({}, entity.tags); // shallow copy
63294 if (preset.tags !== preset.addTags) {
63295 Object.keys(preset.addTags).forEach(function (k) {
63297 if (preset.addTags[k] === '*') {
63298 newTags[k] = 'yes';
63300 newTags[k] = preset.addTags[k];
63307 // Do `wikidata` or `wikipedia` identify this entity as a brand? #6416
63308 // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
63311 if (newTags.wikidata) {
63312 // try matching `wikidata`
63313 isBrand = _nsi.wikidata[newTags.wikidata];
63316 if (!isBrand && newTags.wikipedia) {
63317 // fallback to `wikipedia`
63318 isBrand = _nsi.wikipedia[newTags.wikipedia];
63321 if (isBrand && !newTags.office) {
63322 // but avoid doing this for corporate offices
63323 if (newTags.wikidata) {
63324 newTags['brand:wikidata'] = newTags.wikidata;
63325 delete newTags.wikidata;
63328 if (newTags.wikipedia) {
63329 newTags['brand:wikipedia'] = newTags.wikipedia;
63330 delete newTags.wikipedia;
63331 } // I considered setting `name` and other tags here, but they aren't unique per wikidata
63332 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
63333 // So users will really need to use a preset or assign `name` themselves.
63335 } // try key/value|name match against name-suggestion-index
63338 if (newTags.name) {
63339 for (var i = 0; i < nsiKeys.length; i++) {
63340 var k = nsiKeys[i];
63341 if (!newTags[k]) continue;
63342 var center = entity.extent(graph).center();
63343 var countryCode = iso1A2Code(center);
63345 var match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
63347 if (!match) continue; // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
63349 if (match.d) continue;
63350 var brand = _nsi.brands[match.kvnd];
63352 if (brand && brand.tags['brand:wikidata'] && brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
63353 subtype = 'noncanonical_brand';
63354 var keepTags = ['takeaway'].reduce(function (acc, k) {
63356 acc[k] = newTags[k];
63361 nsiKeys.forEach(function (k) {
63362 return delete newTags[k];
63364 Object.assign(newTags, brand.tags, keepTags);
63369 } // determine diff
63372 var tagDiff = utilTagDiff(oldTags, newTags);
63373 if (!tagDiff.length) return [];
63374 var isOnlyAddingTags = tagDiff.every(function (d) {
63375 return d.type === '+';
63379 if (subtype === 'noncanonical_brand') {
63380 prefix = 'noncanonical_brand.';
63381 } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
63382 subtype = 'incomplete_tags';
63383 prefix = 'incomplete.';
63384 } // don't allow autofixing brand tags
63387 var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
63388 return [new validationIssue({
63391 severity: 'warning',
63392 message: showMessage,
63393 reference: showReference,
63394 entityIds: [entity.id],
63395 hash: JSON.stringify(tagDiff),
63396 dynamicFixes: function dynamicFixes() {
63397 return [new validationIssueFix({
63398 autoArgs: autoArgs,
63399 title: _t.html('issues.fix.upgrade_tags.title'),
63400 onClick: function onClick(context) {
63401 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
63407 function doUpgrade(graph) {
63408 var currEntity = graph.hasEntity(entity.id);
63409 if (!currEntity) return graph;
63410 var newTags = Object.assign({}, currEntity.tags); // shallow copy
63412 tagDiff.forEach(function (diff) {
63413 if (diff.type === '-') {
63414 delete newTags[diff.key];
63415 } else if (diff.type === '+') {
63416 newTags[diff.key] = diff.newVal;
63419 return actionChangeTags(currEntity.id, newTags)(graph);
63422 function showMessage(context) {
63423 var currEntity = context.hasEntity(entity.id);
63424 if (!currEntity) return '';
63425 var messageID = "issues.outdated_tags.".concat(prefix, "message");
63427 if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
63428 messageID += '_incomplete';
63431 return _t.html(messageID, {
63432 feature: utilDisplayLabel(currEntity, context.graph())
63436 function showReference(selection) {
63437 var enter = selection.selectAll('.issue-reference').data([0]).enter();
63438 enter.append('div').attr('class', 'issue-reference').html(_t.html("issues.outdated_tags.".concat(prefix, "reference")));
63439 enter.append('strong').html(_t.html('issues.suggested'));
63440 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) {
63441 var klass = d.type === '+' ? 'add' : 'remove';
63442 return "tagDiff-cell tagDiff-cell-".concat(klass);
63443 }).html(function (d) {
63449 function oldMultipolygonIssues(entity, graph) {
63450 var multipolygon, outerWay;
63452 if (entity.type === 'relation') {
63453 outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
63454 multipolygon = entity;
63455 } else if (entity.type === 'way') {
63456 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
63462 if (!multipolygon || !outerWay) return [];
63463 return [new validationIssue({
63465 subtype: 'old_multipolygon',
63466 severity: 'warning',
63467 message: showMessage,
63468 reference: showReference,
63469 entityIds: [outerWay.id, multipolygon.id],
63470 dynamicFixes: function dynamicFixes() {
63471 return [new validationIssueFix({
63472 autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
63473 title: _t.html('issues.fix.move_tags.title'),
63474 onClick: function onClick(context) {
63475 context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
63481 function doUpgrade(graph) {
63482 var currMultipolygon = graph.hasEntity(multipolygon.id);
63483 var currOuterWay = graph.hasEntity(outerWay.id);
63484 if (!currMultipolygon || !currOuterWay) return graph;
63485 currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
63486 graph = graph.replace(currMultipolygon);
63487 return actionChangeTags(currOuterWay.id, {})(graph);
63490 function showMessage(context) {
63491 var currMultipolygon = context.hasEntity(multipolygon.id);
63492 if (!currMultipolygon) return '';
63493 return _t.html('issues.old_multipolygon.message', {
63494 multipolygon: utilDisplayLabel(currMultipolygon, context.graph())
63498 function showReference(selection) {
63499 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.old_multipolygon.reference'));
63503 var validation = function checkOutdatedTags(entity, graph) {
63504 var issues = oldMultipolygonIssues(entity, graph);
63505 if (!issues.length) issues = oldTagIssues(entity, graph);
63509 validation.type = type;
63513 function validationPrivateData() {
63514 var type = 'private_data'; // assume that some buildings are private
63516 var privateBuildingValues = {
63522 semidetached_house: true,
63523 static_caravan: true
63524 }; // but they might be public if they have one of these other tags
63534 }; // these tags may contain personally identifying info
63536 var personalTags = {
63537 'contact:email': true,
63538 'contact:fax': true,
63539 'contact:phone': true,
63545 var validation = function checkPrivateData(entity) {
63546 var tags = entity.tags;
63547 if (!tags.building || !privateBuildingValues[tags.building]) return [];
63550 for (var k in tags) {
63551 if (publicKeys[k]) return []; // probably a public feature
63553 if (!personalTags[k]) {
63554 keepTags[k] = tags[k];
63558 var tagDiff = utilTagDiff(tags, keepTags);
63559 if (!tagDiff.length) return [];
63560 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
63561 return [new validationIssue({
63563 severity: 'warning',
63564 message: showMessage,
63565 reference: showReference,
63566 entityIds: [entity.id],
63567 dynamicFixes: function dynamicFixes() {
63568 return [new validationIssueFix({
63569 icon: 'iD-operation-delete',
63570 title: _t.html('issues.fix.' + fixID + '.title'),
63571 onClick: function onClick(context) {
63572 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
63578 function doUpgrade(graph) {
63579 var currEntity = graph.hasEntity(entity.id);
63580 if (!currEntity) return graph;
63581 var newTags = Object.assign({}, currEntity.tags); // shallow copy
63583 tagDiff.forEach(function (diff) {
63584 if (diff.type === '-') {
63585 delete newTags[diff.key];
63586 } else if (diff.type === '+') {
63587 newTags[diff.key] = diff.newVal;
63590 return actionChangeTags(currEntity.id, newTags)(graph);
63593 function showMessage(context) {
63594 var currEntity = context.hasEntity(this.entityIds[0]);
63595 if (!currEntity) return '';
63596 return _t.html('issues.private_data.contact.message', {
63597 feature: utilDisplayLabel(currEntity, context.graph())
63601 function showReference(selection) {
63602 var enter = selection.selectAll('.issue-reference').data([0]).enter();
63603 enter.append('div').attr('class', 'issue-reference').html(_t.html('issues.private_data.reference'));
63604 enter.append('strong').html(_t.html('issues.suggested'));
63605 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) {
63606 var klass = d.type === '+' ? 'add' : 'remove';
63607 return 'tagDiff-cell tagDiff-cell-' + klass;
63608 }).html(function (d) {
63614 validation.type = type;
63618 var _discardNameRegexes = [];
63619 function validationSuspiciousName() {
63620 var type = 'suspicious_name';
63621 var keysToTestForGenericValues = ['aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway', 'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway']; // A concern here in switching to async data means that `_nsiFilters` will not
63622 // be available at first, so the data on early tiles may not have tags validated fully.
63624 _mainFileFetcher.get('nsi_filters').then(function (filters) {
63625 // known list of generic names (e.g. "bar")
63626 _discardNameRegexes = filters.discardNames.map(function (discardName) {
63627 return new RegExp(discardName, 'i');
63629 })["catch"](function () {
63633 function isDiscardedSuggestionName(lowercaseName) {
63634 return _discardNameRegexes.some(function (regex) {
63635 return regex.test(lowercaseName);
63637 } // test if the name is just the key or tag value (e.g. "park")
63640 function nameMatchesRawTag(lowercaseName, tags) {
63641 for (var i = 0; i < keysToTestForGenericValues.length; i++) {
63642 var key = keysToTestForGenericValues[i];
63643 var val = tags[key];
63646 val = val.toLowerCase();
63648 if (key === lowercaseName || val === lowercaseName || key.replace(/\_/g, ' ') === lowercaseName || val.replace(/\_/g, ' ') === lowercaseName) {
63657 function isGenericName(name, tags) {
63658 name = name.toLowerCase();
63659 return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
63662 function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
63663 return new validationIssue({
63665 subtype: 'generic_name',
63666 severity: 'warning',
63667 message: function message(context) {
63668 var entity = context.hasEntity(this.entityIds[0]);
63669 if (!entity) return '';
63670 var preset = _mainPresetIndex.match(entity, context.graph());
63671 var langName = langCode && _mainLocalizer.languageName(langCode);
63672 return _t.html('issues.generic_name.message' + (langName ? '_language' : ''), {
63673 feature: preset.name(),
63678 reference: showReference,
63679 entityIds: [entityId],
63680 hash: nameKey + '=' + genericName,
63681 dynamicFixes: function dynamicFixes() {
63682 return [new validationIssueFix({
63683 icon: 'iD-operation-delete',
63684 title: _t.html('issues.fix.remove_the_name.title'),
63685 onClick: function onClick(context) {
63686 var entityId = this.issue.entityIds[0];
63687 var entity = context.entity(entityId);
63688 var tags = Object.assign({}, entity.tags); // shallow copy
63690 delete tags[nameKey];
63691 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation'));
63697 function showReference(selection) {
63698 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
63702 function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
63703 return new validationIssue({
63705 subtype: 'not_name',
63706 severity: 'warning',
63707 message: function message(context) {
63708 var entity = context.hasEntity(this.entityIds[0]);
63709 if (!entity) return '';
63710 var preset = _mainPresetIndex.match(entity, context.graph());
63711 var langName = langCode && _mainLocalizer.languageName(langCode);
63712 return _t.html('issues.incorrect_name.message' + (langName ? '_language' : ''), {
63713 feature: preset.name(),
63714 name: incorrectName,
63718 reference: showReference,
63719 entityIds: [entityId],
63720 hash: nameKey + '=' + incorrectName,
63721 dynamicFixes: function dynamicFixes() {
63722 return [new validationIssueFix({
63723 icon: 'iD-operation-delete',
63724 title: _t.html('issues.fix.remove_the_name.title'),
63725 onClick: function onClick(context) {
63726 var entityId = this.issue.entityIds[0];
63727 var entity = context.entity(entityId);
63728 var tags = Object.assign({}, entity.tags); // shallow copy
63730 delete tags[nameKey];
63731 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation'));
63737 function showReference(selection) {
63738 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
63742 var validation = function checkGenericName(entity) {
63743 // a generic name is okay if it's a known brand or entity
63744 if (entity.hasWikidata()) return [];
63746 var notNames = (entity.tags['not:name'] || '').split(';');
63748 for (var key in entity.tags) {
63749 var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
63751 var langCode = m.length >= 2 ? m[1] : null;
63752 var value = entity.tags[key];
63754 if (notNames.length) {
63755 for (var i in notNames) {
63756 var notName = notNames[i];
63758 if (notName && value === notName) {
63759 issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
63765 if (isGenericName(value, entity.tags)) {
63766 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
63773 validation.type = type;
63777 function validationUnsquareWay(context) {
63778 var type = 'unsquare_way';
63779 var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js
63780 // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
63782 var epsilon = 0.05;
63783 var nodeThreshold = 10;
63785 function isBuilding(entity, graph) {
63786 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
63787 return entity.tags.building && entity.tags.building !== 'no';
63790 var validation = function checkUnsquareWay(entity, graph) {
63791 if (!isBuilding(entity, graph)) return []; // don't flag ways marked as physically unsquare
63793 if (entity.tags.nonsquare === 'yes') return [];
63794 var isClosed = entity.isClosed();
63795 if (!isClosed) return []; // this building has bigger problems
63796 // don't flag ways with lots of nodes since they are likely detail-mapped
63798 var nodes = graph.childNodes(entity).slice(); // shallow copy
63800 if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice
63801 // ignore if not all nodes are fully downloaded
63803 var osm = services.osm;
63804 if (!osm || nodes.some(function (node) {
63805 return !osm.isDataLoaded(node.loc);
63806 })) return []; // don't flag connected ways to avoid unresolvable unsquare loops
63808 var hasConnectedSquarableWays = nodes.some(function (node) {
63809 return graph.parentWays(node).some(function (way) {
63810 if (way.id === entity.id) return false;
63811 if (isBuilding(way, graph)) return true;
63812 return graph.parentRelations(way).some(function (parentRelation) {
63813 return parentRelation.isMultipolygon() && parentRelation.tags.building && parentRelation.tags.building !== 'no';
63817 if (hasConnectedSquarableWays) return []; // user-configurable square threshold
63819 var storedDegreeThreshold = corePreferences('validate-square-degrees');
63820 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
63821 var points = nodes.map(function (node) {
63822 return context.projection(node.loc);
63824 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
63825 var autoArgs; // don't allow autosquaring features linked to wikidata
63827 if (!entity.tags.wikidata) {
63828 // use same degree threshold as for detection
63829 var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
63830 autoAction.transitionable = false; // when autofixing, do it instantly
63832 autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature', {
63837 return [new validationIssue({
63839 subtype: 'building',
63840 severity: 'warning',
63841 message: function message(context) {
63842 var entity = context.hasEntity(this.entityIds[0]);
63843 return entity ? _t.html('issues.unsquare_way.message', {
63844 feature: utilDisplayLabel(entity, context.graph())
63847 reference: showReference,
63848 entityIds: [entity.id],
63849 hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
63850 dynamicFixes: function dynamicFixes() {
63851 return [new validationIssueFix({
63852 icon: 'iD-operation-orthogonalize',
63853 title: _t.html('issues.fix.square_feature.title'),
63854 autoArgs: autoArgs,
63855 onClick: function onClick(context, completionHandler) {
63856 var entityId = this.issue.entityIds[0]; // use same degree threshold as for detection
63858 context.perform(actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), _t('operations.orthogonalize.annotation.feature', {
63860 })); // run after the squaring transition (currently 150ms)
63862 window.setTimeout(function () {
63863 completionHandler();
63868 new validationIssueFix({
63869 title: t.html('issues.fix.tag_as_unsquare.title'),
63870 onClick: function(context) {
63871 var entityId = this.issue.entityIds[0];
63872 var entity = context.entity(entityId);
63873 var tags = Object.assign({}, entity.tags); // shallow copy
63874 tags.nonsquare = 'yes';
63876 actionChangeTags(entityId, tags),
63877 t('issues.fix.tag_as_unsquare.annotation')
63886 function showReference(selection) {
63887 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unsquare_way.buildings.reference'));
63891 validation.type = type;
63895 var Validations = /*#__PURE__*/Object.freeze({
63897 validationAlmostJunction: validationAlmostJunction,
63898 validationCloseNodes: validationCloseNodes,
63899 validationCrossingWays: validationCrossingWays,
63900 validationDisconnectedWay: validationDisconnectedWay,
63901 validationFormatting: validationFormatting,
63902 validationHelpRequest: validationHelpRequest,
63903 validationImpossibleOneway: validationImpossibleOneway,
63904 validationIncompatibleSource: validationIncompatibleSource,
63905 validationMaprules: validationMaprules,
63906 validationMismatchedGeometry: validationMismatchedGeometry,
63907 validationMissingRole: validationMissingRole,
63908 validationMissingTag: validationMissingTag,
63909 validationOutdatedTags: validationOutdatedTags,
63910 validationPrivateData: validationPrivateData,
63911 validationSuspiciousName: validationSuspiciousName,
63912 validationUnsquareWay: validationUnsquareWay
63915 function coreValidator(context) {
63916 var dispatch$1 = dispatch('validated', 'focusedIssue');
63917 var validator = utilRebind({}, dispatch$1, 'on');
63919 var _disabledRules = {};
63920 var _ignoredIssueIDs = {}; // issue.id -> true
63922 var _baseCache = validationCache(); // issues before any user edits
63925 var _headCache = validationCache(); // issues after all user edits
63928 var _validatedGraph = null;
63930 var _deferred = new Set(); //
63931 // initialize the validator rulesets
63935 validator.init = function () {
63936 Object.values(Validations).forEach(function (validation) {
63937 if (typeof validation !== 'function') return;
63938 var fn = validation(context);
63942 var disabledRules = corePreferences('validate-disabledRules');
63944 if (disabledRules) {
63945 disabledRules.split(',').forEach(function (key) {
63946 _disabledRules[key] = true;
63951 function reset(resetIgnored) {
63952 Array.from(_deferred).forEach(function (handle) {
63953 window.cancelIdleCallback(handle);
63955 _deferred["delete"](handle);
63956 }); // clear caches
63958 if (resetIgnored) _ignoredIssueIDs = {};
63959 _baseCache = validationCache();
63960 _headCache = validationCache();
63961 _validatedGraph = null;
63963 // clear caches, called whenever iD resets after a save
63967 validator.reset = function () {
63971 validator.resetIgnoredIssues = function () {
63972 _ignoredIssueIDs = {}; // reload UI
63974 dispatch$1.call('validated');
63975 }; // must update issues when the user changes the unsquare thereshold
63978 validator.reloadUnsquareIssues = function () {
63979 reloadUnsquareIssues(_headCache, context.graph());
63980 reloadUnsquareIssues(_baseCache, context.history().base());
63981 dispatch$1.call('validated');
63984 function reloadUnsquareIssues(cache, graph) {
63985 var checkUnsquareWay = _rules.unsquare_way;
63986 if (typeof checkUnsquareWay !== 'function') return; // uncache existing
63988 cache.uncacheIssuesOfType('unsquare_way');
63989 var buildings = context.history().tree().intersects(geoExtent([-180, -90], [180, 90]), graph) // everywhere
63990 .filter(function (entity) {
63991 return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
63992 }); // rerun for all buildings
63994 buildings.forEach(function (entity) {
63995 var detected = checkUnsquareWay(entity, graph);
63996 if (detected.length !== 1) return;
63997 var issue = detected[0];
63999 if (!cache.issuesByEntityID[entity.id]) {
64000 cache.issuesByEntityID[entity.id] = new Set();
64003 cache.issuesByEntityID[entity.id].add(issue.id);
64004 cache.issuesByIssueID[issue.id] = issue;
64007 // what: 'all', // 'all' or 'edited'
64008 // where: 'all', // 'all' or 'visible'
64009 // includeIgnored: false // true, false, or 'only'
64010 // includeDisabledRules: false // true, false, or 'only'
64014 validator.getIssues = function (options) {
64015 var opts = Object.assign({
64018 includeIgnored: false,
64019 includeDisabledRules: false
64021 var issues = Object.values(_headCache.issuesByIssueID);
64022 var view = context.map().extent();
64023 return issues.filter(function (issue) {
64024 if (!issue) return false;
64025 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
64026 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
64027 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
64028 if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false; // Sanity check: This issue may be for an entity that not longer exists.
64029 // If we detect this, uncache and return false so it is not included..
64031 var entityIds = issue.entityIds || [];
64033 for (var i = 0; i < entityIds.length; i++) {
64034 var entityId = entityIds[i];
64036 if (!context.hasEntity(entityId)) {
64037 delete _headCache.issuesByEntityID[entityId];
64038 delete _headCache.issuesByIssueID[issue.id];
64043 if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) return false;
64045 if (opts.where === 'visible') {
64046 var extent = issue.extent(context.graph());
64047 if (!view.intersects(extent)) return false;
64054 validator.getResolvedIssues = function () {
64055 var baseIssues = Object.values(_baseCache.issuesByIssueID);
64056 return baseIssues.filter(function (issue) {
64057 return !_headCache.issuesByIssueID[issue.id];
64061 validator.focusIssue = function (issue) {
64062 var extent = issue.extent(context.graph());
64065 var setZoom = Math.max(context.map().zoom(), 19);
64066 context.map().unobscuredCenterZoomEase(extent.center(), setZoom); // select the first entity
64068 if (issue.entityIds && issue.entityIds.length) {
64069 window.setTimeout(function () {
64070 var ids = issue.entityIds;
64071 context.enter(modeSelect(context, [ids[0]]));
64072 dispatch$1.call('focusedIssue', this, issue);
64073 }, 250); // after ease
64078 validator.getIssuesBySeverity = function (options) {
64079 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
64080 groups.error = groups.error || [];
64081 groups.warning = groups.warning || [];
64083 }; // show some issue types in a particular order
64086 var orderedIssueTypes = [// flag missing data first
64087 'missing_tag', 'missing_role', // then flag identity issues
64088 'outdated_tags', 'mismatched_geometry', // flag geometry issues where fixing them might solve connectivity issues
64089 'crossing_ways', 'almost_junction', // then flag connectivity issues
64090 'disconnected_way', 'impossible_oneway']; // returns the issues that the given entity IDs have in common, matching the given options
64092 validator.getSharedEntityIssues = function (entityIDs, options) {
64093 var cache = _headCache; // gather the issues that are common to all the entities
64095 var issueIDs = entityIDs.reduce(function (acc, entityID) {
64096 var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
64099 return new Set(entityIssueIDs);
64102 return new Set(_toConsumableArray(acc).filter(function (elem) {
64103 return entityIssueIDs.has(elem);
64106 var opts = options || {};
64107 return Array.from(issueIDs).map(function (id) {
64108 return cache.issuesByIssueID[id];
64109 }).filter(function (issue) {
64110 if (!issue) return false;
64111 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
64112 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
64113 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
64114 if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false;
64116 }).sort(function (issue1, issue2) {
64117 if (issue1.type === issue2.type) {
64118 // issues of the same type, sort deterministically
64119 return issue1.id < issue2.id ? -1 : 1;
64122 var index1 = orderedIssueTypes.indexOf(issue1.type);
64123 var index2 = orderedIssueTypes.indexOf(issue2.type);
64125 if (index1 !== -1 && index2 !== -1) {
64126 // both issue types have explicit sort orders
64127 return index1 - index2;
64128 } else if (index1 === -1 && index2 === -1) {
64129 // neither issue type has an explicit sort order, sort by type
64130 return issue1.type < issue2.type ? -1 : 1;
64132 // order explicit types before everything else
64133 return index1 !== -1 ? -1 : 1;
64138 validator.getEntityIssues = function (entityID, options) {
64139 return validator.getSharedEntityIssues([entityID], options);
64142 validator.getRuleKeys = function () {
64143 return Object.keys(_rules);
64146 validator.isRuleEnabled = function (key) {
64147 return !_disabledRules[key];
64150 validator.toggleRule = function (key) {
64151 if (_disabledRules[key]) {
64152 delete _disabledRules[key];
64154 _disabledRules[key] = true;
64157 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
64158 validator.validate();
64161 validator.disableRules = function (keys) {
64162 _disabledRules = {};
64163 keys.forEach(function (k) {
64164 _disabledRules[k] = true;
64166 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
64167 validator.validate();
64170 validator.ignoreIssue = function (id) {
64171 _ignoredIssueIDs[id] = true;
64173 // Run validation on a single entity for the given graph
64177 function validateEntity(entity, graph) {
64178 var entityIssues = []; // runs validation and appends resulting issues
64180 function runValidation(key) {
64181 var fn = _rules[key];
64183 if (typeof fn !== 'function') {
64184 console.error('no such validation rule = ' + key); // eslint-disable-line no-console
64189 var detected = fn(entity, graph);
64190 entityIssues = entityIssues.concat(detected);
64194 Object.keys(_rules).forEach(runValidation);
64195 return entityIssues;
64198 function entityIDsToValidate(entityIDs, graph) {
64199 var processedIDs = new Set();
64200 return entityIDs.reduce(function (acc, entityID) {
64201 // keep redundancy check separate from `acc` because an `entityID`
64202 // could have been added to `acc` as a related entity through an earlier pass
64203 if (processedIDs.has(entityID)) return acc;
64204 processedIDs.add(entityID);
64205 var entity = graph.hasEntity(entityID);
64206 if (!entity) return acc;
64208 var checkParentRels = [entity];
64210 if (entity.type === 'node') {
64211 graph.parentWays(entity).forEach(function (parentWay) {
64212 acc.add(parentWay.id); // include parent ways
64214 checkParentRels.push(parentWay);
64216 } else if (entity.type === 'relation') {
64217 entity.members.forEach(function (member) {
64218 acc.add(member.id); // include members
64220 } else if (entity.type === 'way') {
64221 entity.nodes.forEach(function (nodeID) {
64222 acc.add(nodeID); // include child nodes
64224 graph._parentWays[nodeID].forEach(function (wayID) {
64225 acc.add(wayID); // include connected ways
64230 checkParentRels.forEach(function (entity) {
64231 // include parent relations
64232 if (entity.type !== 'relation') {
64233 // but not super-relations
64234 graph.parentRelations(entity).forEach(function (parentRelation) {
64235 acc.add(parentRelation.id);
64242 // Run validation for several entities, supplied `entityIDs`,
64243 // against `graph` for the given `cache`
64247 function validateEntities(entityIDs, graph, cache) {
64248 // clear caches for existing issues related to these entities
64249 entityIDs.forEach(cache.uncacheEntityID); // detect new issues and update caches
64251 entityIDs.forEach(function (entityID) {
64252 var entity = graph.hasEntity(entityID); // don't validate deleted entities
64254 if (!entity) return;
64255 var issues = validateEntity(entity, graph);
64256 cache.cacheIssues(issues);
64259 // Validates anything that has changed since the last time it was run.
64260 // Also updates the "validatedGraph" to be the current graph
64261 // and dispatches a `validated` event when finished.
64265 validator.validate = function () {
64266 var currGraph = context.graph();
64267 _validatedGraph = _validatedGraph || context.history().base();
64269 if (currGraph === _validatedGraph) {
64270 dispatch$1.call('validated');
64274 var oldGraph = _validatedGraph;
64275 var difference = coreDifference(oldGraph, currGraph);
64276 _validatedGraph = currGraph;
64277 var createdAndModifiedEntityIDs = difference.extantIDs(true); // created/modified (true = w/relation members)
64279 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph); // check modified and deleted entities against the old graph in order to update their related entities
64280 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
64282 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified()).map(function (entity) {
64285 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph); // concat the sets
64287 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
64288 validateEntities(entityIDsToCheck, context.graph(), _headCache);
64289 dispatch$1.call('validated');
64292 context.history().on('reset.validator', function () {
64293 // cached issues aren't valid any longer if the history has been reset
64295 validator.validate();
64296 }); // WHEN TO RUN VALIDATION:
64297 // When graph changes:
64299 context.history().on('restore.validator', validator.validate) // restore saved history
64300 .on('undone.validator', validator.validate) // undo
64301 .on('redone.validator', validator.validate); // redo
64302 // but not on 'change' (e.g. while drawing)
64303 // When user changes editing modes:
64305 context.on('exit.validator', validator.validate); // When merging fetched data:
64307 context.history().on('merge.validator', function (entities) {
64308 if (!entities) return;
64309 var handle = window.requestIdleCallback(function () {
64310 var entityIDs = entities.map(function (entity) {
64313 var headGraph = context.graph();
64314 validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
64315 var baseGraph = context.history().base();
64316 validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
64317 dispatch$1.call('validated');
64320 _deferred.add(handle);
64325 function validationCache() {
64327 issuesByIssueID: {},
64328 // issue.id -> issue
64329 issuesByEntityID: {} // entity.id -> set(issue.id)
64333 cache.cacheIssues = function (issues) {
64334 issues.forEach(function (issue) {
64335 var entityIds = issue.entityIds || [];
64336 entityIds.forEach(function (entityId) {
64337 if (!cache.issuesByEntityID[entityId]) {
64338 cache.issuesByEntityID[entityId] = new Set();
64341 cache.issuesByEntityID[entityId].add(issue.id);
64343 cache.issuesByIssueID[issue.id] = issue;
64347 cache.uncacheIssue = function (issue) {
64348 // When multiple entities are involved (e.g. crossing_ways),
64349 // remove this issue from the other entity caches too..
64350 var entityIds = issue.entityIds || [];
64351 entityIds.forEach(function (entityId) {
64352 if (cache.issuesByEntityID[entityId]) {
64353 cache.issuesByEntityID[entityId]["delete"](issue.id);
64356 delete cache.issuesByIssueID[issue.id];
64359 cache.uncacheIssues = function (issues) {
64360 issues.forEach(cache.uncacheIssue);
64363 cache.uncacheIssuesOfType = function (type) {
64364 var issuesOfType = Object.values(cache.issuesByIssueID).filter(function (issue) {
64365 return issue.type === type;
64367 cache.uncacheIssues(issuesOfType);
64369 // Remove a single entity and all its related issues from the caches
64373 cache.uncacheEntityID = function (entityID) {
64374 var issueIDs = cache.issuesByEntityID[entityID];
64375 if (!issueIDs) return;
64376 issueIDs.forEach(function (issueID) {
64377 var issue = cache.issuesByIssueID[issueID];
64380 cache.uncacheIssue(issue);
64382 delete cache.issuesByIssueID[issueID];
64385 delete cache.issuesByEntityID[entityID];
64391 function coreUploader(context) {
64392 var dispatch$1 = dispatch( // Start and end events are dispatched exactly once each per legitimate outside call to `save`
64393 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
64394 'saveEnded', // dispatched after the result event has been dispatched
64395 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
64396 'progressChanged', // Each save results in one of these outcomes:
64397 'resultNoChanges', // upload wasn't attempted since there were no edits
64398 'resultErrors', // upload failed due to errors
64399 'resultConflicts', // upload failed due to data conflicts
64400 'resultSuccess' // upload completed without errors
64402 var _isSaving = false;
64403 var _conflicts = [];
64408 var _discardTags = {};
64409 _mainFileFetcher.get('discarded').then(function (d) {
64411 })["catch"](function () {
64414 var uploader = utilRebind({}, dispatch$1, 'on');
64416 uploader.isSaving = function () {
64420 uploader.save = function (changeset, tryAgain, checkConflicts) {
64421 // Guard against accidentally entering save code twice - #4641
64422 if (_isSaving && !tryAgain) {
64426 var osm = context.connection();
64427 if (!osm) return; // If user somehow got logged out mid-save, try to reauthenticate..
64428 // This can happen if they were logged in from before, but the tokens are no longer valid.
64430 if (!osm.authenticated()) {
64431 osm.authenticate(function (err) {
64433 uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off..
64441 dispatch$1.call('saveStarted', this);
64444 var history = context.history();
64446 _errors = []; // Store original changes, in case user wants to download them as an .osc file
64448 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); // First time, `history.perform` a no-op action.
64449 // Any conflict resolutions will be done as `history.replace`
64450 // Remember to pop this later if needed
64453 history.perform(actionNoop());
64454 } // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
64457 if (!checkConflicts) {
64458 upload(changeset); // Do the full (slow) conflict check..
64460 performFullConflictCheck(changeset);
64464 function performFullConflictCheck(changeset) {
64465 var osm = context.connection();
64467 var history = context.history();
64468 var localGraph = context.graph();
64469 var remoteGraph = coreGraph(history.base(), true);
64470 var summary = history.difference().summary();
64473 for (var i = 0; i < summary.length; i++) {
64474 var item = summary[i];
64476 if (item.changeType === 'modified') {
64477 _toCheck.push(item.entity.id);
64481 var _toLoad = withChildNodes(_toCheck, localGraph);
64484 var _toLoadCount = 0;
64485 var _toLoadTotal = _toLoad.length;
64487 if (_toCheck.length) {
64488 dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
64490 _toLoad.forEach(function (id) {
64491 _loaded[id] = false;
64494 osm.loadMultiple(_toLoad, loaded);
64501 function withChildNodes(ids, graph) {
64502 var s = new Set(ids);
64503 ids.forEach(function (id) {
64504 var entity = graph.entity(id);
64505 if (entity.type !== 'way') return;
64506 graph.childNodes(entity).forEach(function (child) {
64507 if (child.version !== undefined) {
64512 return Array.from(s);
64513 } // Reload modified entities into an alternate graph and check for conflicts..
64516 function loaded(err, result) {
64517 if (_errors.length) return;
64521 msg: err.message || err.responseText,
64522 details: [_t('save.status_code', {
64527 didResultInErrors();
64530 result.data.forEach(function (entity) {
64531 remoteGraph.replace(entity);
64532 _loaded[entity.id] = true;
64533 _toLoad = _toLoad.filter(function (val) {
64534 return val !== entity.id;
64536 if (!entity.visible) return; // Because loadMultiple doesn't download /full like loadEntity,
64537 // need to also load children that aren't already being checked..
64541 if (entity.type === 'way') {
64542 for (i = 0; i < entity.nodes.length; i++) {
64543 id = entity.nodes[i];
64545 if (_loaded[id] === undefined) {
64546 _loaded[id] = false;
64550 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
64551 for (i = 0; i < entity.members.length; i++) {
64552 id = entity.members[i].id;
64554 if (_loaded[id] === undefined) {
64555 _loaded[id] = false;
64561 _toLoadCount += result.data.length;
64562 _toLoadTotal += loadMore.length;
64563 dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
64565 if (loadMore.length) {
64566 _toLoad.push.apply(_toLoad, loadMore);
64568 osm.loadMultiple(loadMore, loaded);
64571 if (!_toLoad.length) {
64578 function detectConflicts() {
64579 function choice(id, text, _action) {
64583 action: function action() {
64584 history.replace(_action);
64589 function formatUser(d) {
64590 return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
64593 function entityName(entity) {
64594 return utilDisplayName(entity) || utilDisplayType(entity.id) + ' ' + entity.id;
64597 function sameVersions(local, remote) {
64598 if (local.version !== remote.version) return false;
64600 if (local.type === 'way') {
64601 var children = utilArrayUnion(local.nodes, remote.nodes);
64603 for (var i = 0; i < children.length; i++) {
64604 var a = localGraph.hasEntity(children[i]);
64605 var b = remoteGraph.hasEntity(children[i]);
64606 if (a && b && a.version !== b.version) return false;
64613 _toCheck.forEach(function (id) {
64614 var local = localGraph.entity(id);
64615 var remote = remoteGraph.entity(id);
64616 if (sameVersions(local, remote)) return;
64617 var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
64618 history.replace(merge);
64619 var mergeConflicts = merge.conflicts();
64620 if (!mergeConflicts.length) return; // merged safely
64622 var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
64623 var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
64624 var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
64625 var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
64629 name: entityName(local),
64630 details: mergeConflicts,
64632 choices: [choice(id, keepMine, forceLocal), choice(id, keepTheirs, forceRemote)]
64638 function upload(changeset) {
64639 var osm = context.connection();
64643 msg: 'No OSM Service'
64647 if (_conflicts.length) {
64648 didResultInConflicts(changeset);
64649 } else if (_errors.length) {
64650 didResultInErrors();
64652 var history = context.history();
64653 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
64655 if (changes.modified.length || changes.created.length || changes.deleted.length) {
64656 dispatch$1.call('willAttemptUpload', this);
64657 osm.putChangeset(changeset, changes, uploadCallback);
64659 // changes were insignificant or reverted by user
64660 didResultInNoChanges();
64665 function uploadCallback(err, changeset) {
64667 if (err.status === 409) {
64669 uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true
64672 msg: err.message || err.responseText,
64673 details: [_t('save.status_code', {
64678 didResultInErrors();
64681 didResultInSuccess(changeset);
64685 function didResultInNoChanges() {
64686 dispatch$1.call('resultNoChanges', this);
64688 context.flush(); // reset iD
64691 function didResultInErrors() {
64692 context.history().pop();
64693 dispatch$1.call('resultErrors', this, _errors);
64697 function didResultInConflicts(changeset) {
64698 _conflicts.sort(function (a, b) {
64699 return b.id.localeCompare(a.id);
64702 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
64706 function didResultInSuccess(changeset) {
64707 // delete the edit stack cached to local storage
64708 context.history().clearSaved();
64709 dispatch$1.call('resultSuccess', this, changeset); // Add delay to allow for postgres replication #1646 #2678
64711 window.setTimeout(function () {
64713 context.flush(); // reset iD
64717 function endSave() {
64719 dispatch$1.call('saveEnded', this);
64722 uploader.cancelConflictResolution = function () {
64723 context.history().pop();
64726 uploader.processResolvedConflicts = function (changeset) {
64727 var history = context.history();
64729 for (var i = 0; i < _conflicts.length; i++) {
64730 if (_conflicts[i].chosen === 1) {
64731 // user chose "use theirs"
64732 var entity = context.hasEntity(_conflicts[i].id);
64734 if (entity && entity.type === 'way') {
64735 var children = utilArrayUniq(entity.nodes);
64737 for (var j = 0; j < children.length; j++) {
64738 history.replace(actionRevert(children[j]));
64742 history.replace(actionRevert(_conflicts[i].id));
64746 uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false
64749 uploader.reset = function () {};
64754 var abs$4 = Math.abs;
64755 var exp$2 = Math.exp;
64758 var FORCED$g = fails(function () {
64759 return Math.sinh(-2e-17) != -2e-17;
64762 // `Math.sinh` method
64763 // https://tc39.github.io/ecma262/#sec-math.sinh
64764 // V8 near Chromium 38 has a problem with very small numbers
64765 _export({ target: 'Math', stat: true, forced: FORCED$g }, {
64766 sinh: function sinh(x) {
64767 return abs$4(x = +x) < 1 ? (mathExpm1(x) - mathExpm1(-x)) / 2 : (exp$2(x - 1) - exp$2(-x - 1)) * (E / 2);
64771 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
64773 window.matchMedia("\n (-webkit-min-device-pixel-ratio: 2), /* Safari */\n (min-resolution: 2dppx), /* standard */\n (min-resolution: 192dpi) /* fallback */\n ").addListener(function () {
64774 isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
64777 function localeDateString(s) {
64778 if (!s) return null;
64784 var d = new Date(s);
64785 if (isNaN(d.getTime())) return null;
64786 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
64789 function vintageRange(vintage) {
64792 if (vintage.start || vintage.end) {
64793 s = vintage.start || '?';
64795 if (vintage.start !== vintage.end) {
64796 s += ' - ' + (vintage.end || '?');
64803 function rendererBackgroundSource(data) {
64804 var source = Object.assign({}, data); // shallow copy
64806 var _offset = [0, 0];
64807 var _name = source.name;
64808 var _description = source.description;
64810 var _best = !!source.best;
64812 var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
64814 source.tileSize = data.tileSize || 256;
64815 source.zoomExtent = data.zoomExtent || [0, 22];
64816 source.overzoom = data.overzoom !== false;
64818 source.offset = function (val) {
64819 if (!arguments.length) return _offset;
64824 source.nudge = function (val, zoomlevel) {
64825 _offset[0] += val[0] / Math.pow(2, zoomlevel);
64826 _offset[1] += val[1] / Math.pow(2, zoomlevel);
64830 source.name = function () {
64831 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64832 return _t('imagery.' + id_safe + '.name', {
64837 source.label = function () {
64838 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64839 return _t.html('imagery.' + id_safe + '.name', {
64844 source.description = function () {
64845 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64846 return _t.html('imagery.' + id_safe + '.description', {
64847 "default": _description
64851 source.best = function () {
64855 source.area = function () {
64856 if (!data.polygon) return Number.MAX_VALUE; // worldwide
64858 var area = d3_geoArea({
64859 type: 'MultiPolygon',
64860 coordinates: [data.polygon]
64862 return isNaN(area) ? 0 : area;
64865 source.imageryUsed = function () {
64866 return _name || source.id;
64869 source.template = function (val) {
64870 if (!arguments.length) return _template;
64872 if (source.id === 'custom') {
64879 source.url = function (coord) {
64880 var result = _template;
64881 if (result === '') return result; // source 'none'
64882 // Guess a type based on the tokens present in the template
64883 // (This is for 'custom' source, where we don't know)
64885 if (!source.type) {
64886 if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
64887 source.type = 'wms';
64888 source.projection = 'EPSG:3857'; // guess
64889 } else if (/\{(x|y)\}/.test(_template)) {
64890 source.type = 'tms';
64891 } else if (/\{u\}/.test(_template)) {
64892 source.type = 'bing';
64896 if (source.type === 'wms') {
64897 var tileToProjectedCoords = function tileToProjectedCoords(x, y, z) {
64898 //polyfill for IE11, PhantomJS
64899 var sinh = Math.sinh || function (x) {
64900 var y = Math.exp(x);
64901 return (y - 1 / y) / 2;
64904 var zoomSize = Math.pow(2, z);
64905 var lon = x / zoomSize * Math.PI * 2 - Math.PI;
64906 var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
64908 switch (source.projection) {
64911 x: lon * 180 / Math.PI,
64912 y: lat * 180 / Math.PI
64916 // EPSG:3857 and synonyms
64917 var mercCoords = mercatorRaw(lon, lat);
64919 x: 20037508.34 / Math.PI * mercCoords[0],
64920 y: 20037508.34 / Math.PI * mercCoords[1]
64925 var tileSize = source.tileSize;
64926 var projection = source.projection;
64927 var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
64928 var maxXminY = tileToProjectedCoords(coord[0] + 1, coord[1] + 1, coord[2]);
64929 result = result.replace(/\{(\w+)\}/g, function (token, key) {
64939 return projection.replace(/^EPSG:/, '');
64942 // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
64943 if (projection === 'EPSG:4326' && // The CRS parameter implies version 1.3 (prior versions use SRS)
64944 /VERSION=1.3|CRS={proj}/.test(source.template())) {
64945 return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
64947 return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
64966 } else if (source.type === 'tms') {
64967 result = result.replace('{x}', coord[0]).replace('{y}', coord[1]) // TMS-flipped y coordinate
64968 .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1).replace(/\{z(oom)?\}/, coord[2]) // only fetch retina tiles for retina screens
64969 .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
64970 } else if (source.type === 'bing') {
64971 result = result.replace('{u}', function () {
64974 for (var zoom = coord[2]; zoom > 0; zoom--) {
64976 var mask = 1 << zoom - 1;
64977 if ((coord[0] & mask) !== 0) b++;
64978 if ((coord[1] & mask) !== 0) b += 2;
64984 } // these apply to any type..
64987 result = result.replace(/\{switch:([^}]+)\}/, function (s, r) {
64988 var subdomains = r.split(',');
64989 return subdomains[(coord[0] + coord[1]) % subdomains.length];
64994 source.validZoom = function (z) {
64995 return source.zoomExtent[0] <= z && (source.overzoom || source.zoomExtent[1] > z);
64998 source.isLocatorOverlay = function () {
64999 return source.id === 'mapbox_locator_overlay';
65001 /* hides a source from the list, but leaves it available for use */
65004 source.isHidden = function () {
65005 return source.id === 'DigitalGlobe-Premium-vintage' || source.id === 'DigitalGlobe-Standard-vintage';
65008 source.copyrightNotices = function () {};
65010 source.getMetadata = function (center, tileCoord, callback) {
65012 start: localeDateString(source.startDate),
65013 end: localeDateString(source.endDate)
65015 vintage.range = vintageRange(vintage);
65019 callback(null, metadata);
65025 rendererBackgroundSource.Bing = function (data, dispatch) {
65026 // http://msdn.microsoft.com/en-us/library/ff701716.aspx
65027 // http://msdn.microsoft.com/en-us/library/ff701701.aspx
65028 data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
65029 var bing = rendererBackgroundSource(data); // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
65031 var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD
65033 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
65036 var providers = [];
65037 d3_json(url).then(function (json) {
65038 providers = json.resourceSets[0].resources[0].imageryProviders.map(function (provider) {
65040 attribution: provider.attribution,
65041 areas: provider.coverageAreas.map(function (area) {
65043 zoom: [area.zoomMin, area.zoomMax],
65044 extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
65049 dispatch.call('change');
65050 })["catch"](function () {
65054 bing.copyrightNotices = function (zoom, extent) {
65055 zoom = Math.min(zoom, 21);
65056 return providers.filter(function (provider) {
65057 return provider.areas.some(function (area) {
65058 return extent.intersects(area.extent) && area.zoom[0] <= zoom && area.zoom[1] >= zoom;
65060 }).map(function (provider) {
65061 return provider.attribution;
65065 bing.getMetadata = function (center, tileCoord, callback) {
65066 var tileID = tileCoord.slice(0, 3).join('/');
65067 var zoom = Math.min(tileCoord[2], 21);
65068 var centerPoint = center[1] + ',' + center[0]; // lat,lng
65070 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key;
65071 if (inflight[tileID]) return;
65073 if (!cache[tileID]) {
65074 cache[tileID] = {};
65077 if (cache[tileID] && cache[tileID].metadata) {
65078 return callback(null, cache[tileID].metadata);
65081 inflight[tileID] = true;
65082 d3_json(url).then(function (result) {
65083 delete inflight[tileID];
65086 throw new Error('Unknown Error');
65090 start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
65091 end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
65093 vintage.range = vintageRange(vintage);
65097 cache[tileID].metadata = metadata;
65098 if (callback) callback(null, metadata);
65099 })["catch"](function (err) {
65100 delete inflight[tileID];
65101 if (callback) callback(err.message);
65105 bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
65109 rendererBackgroundSource.Esri = function (data) {
65110 // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
65111 if (data.template.match(/blankTile/) === null) {
65112 data.template = data.template + '?blankTile=false';
65115 var esri = rendererBackgroundSource(data);
65119 var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically
65120 // https://developers.arcgis.com/documentation/tiled-elevation-service/
65123 esri.fetchTilemap = function (center) {
65124 // skip if we have already fetched a tilemap within 5km
65125 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
65126 _prevCenter = center; // tiles are available globally to zoom level 19, afterward they may or may not be present
65128 var z = 20; // first generate a random url using the template
65130 var dummyUrl = esri.url([1, 2, 3]); // calculate url z/y/x from the lat/long of the center of the map
65132 var x = Math.floor((center[0] + 180) / 360 * Math.pow(2, z));
65133 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
65135 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
65137 d3_json(tilemapUrl).then(function (tilemap) {
65139 throw new Error('Unknown Error');
65142 var hasTiles = true;
65144 for (var i = 0; i < tilemap.data.length; i++) {
65145 // 0 means an individual tile in the grid doesn't exist
65146 if (!tilemap.data[i]) {
65150 } // if any tiles are missing at level 20 we restrict maxZoom to 19
65153 esri.zoomExtent[1] = hasTiles ? 22 : 19;
65154 })["catch"](function () {
65159 esri.getMetadata = function (center, tileCoord, callback) {
65160 var tileID = tileCoord.slice(0, 3).join('/');
65161 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
65162 var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
65164 var unknown = _t('info_panels.background.unknown');
65168 if (inflight[tileID]) return;
65171 case zoom >= 20 && esri.id === 'EsriWorldImageryClarity':
65188 metadataLayer = 99;
65191 var url; // build up query using the layer appropriate to the current zoom
65193 if (esri.id === 'EsriWorldImagery') {
65194 url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
65195 } else if (esri.id === 'EsriWorldImageryClarity') {
65196 url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
65199 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
65201 if (!cache[tileID]) {
65202 cache[tileID] = {};
65205 if (cache[tileID] && cache[tileID].metadata) {
65206 return callback(null, cache[tileID].metadata);
65207 } // accurate metadata is only available >= 13
65210 if (metadataLayer === 99) {
65219 description: unknown,
65220 resolution: unknown,
65223 callback(null, metadata);
65225 inflight[tileID] = true;
65226 d3_json(url).then(function (result) {
65227 delete inflight[tileID];
65230 throw new Error('Unknown Error');
65231 } else if (result.features && result.features.length < 1) {
65232 throw new Error('No Results');
65233 } else if (result.error && result.error.message) {
65234 throw new Error(result.error.message);
65235 } // pass through the discrete capture date from metadata
65238 var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
65240 start: captureDate,
65246 source: clean(result.features[0].attributes.NICE_NAME),
65247 description: clean(result.features[0].attributes.NICE_DESC),
65248 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
65249 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
65250 }; // append units - meters
65252 if (isFinite(metadata.resolution)) {
65253 metadata.resolution += ' m';
65256 if (isFinite(metadata.accuracy)) {
65257 metadata.accuracy += ' m';
65260 cache[tileID].metadata = metadata;
65261 if (callback) callback(null, metadata);
65262 })["catch"](function (err) {
65263 delete inflight[tileID];
65264 if (callback) callback(err.message);
65268 function clean(val) {
65269 return String(val).trim() || unknown;
65276 rendererBackgroundSource.None = function () {
65277 var source = rendererBackgroundSource({
65282 source.name = function () {
65283 return _t('background.none');
65286 source.label = function () {
65287 return _t.html('background.none');
65290 source.imageryUsed = function () {
65294 source.area = function () {
65295 return -1; // sources in background pane are sorted by area
65301 rendererBackgroundSource.Custom = function (template) {
65302 var source = rendererBackgroundSource({
65307 source.name = function () {
65308 return _t('background.custom');
65311 source.label = function () {
65312 return _t.html('background.custom');
65315 source.imageryUsed = function () {
65316 // sanitize personal connection tokens - #6801
65317 var cleaned = source.template(); // from query string parameters
65319 if (cleaned.indexOf('?') !== -1) {
65320 var parts = cleaned.split('?', 2);
65321 var qs = utilStringQs(parts[1]);
65322 ['access_token', 'connectId', 'token'].forEach(function (param) {
65324 qs[param] = '{apikey}';
65327 cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode
65328 } // from wms/wmts api path parameters
65331 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
65332 return 'Custom (' + cleaned + ' )';
65335 source.area = function () {
65336 return -2; // sources in background pane are sorted by area
65342 function rendererTileLayer(context) {
65343 var transformProp = utilPrefixCSSProperty('Transform');
65344 var tiler = utilTiler();
65345 var _tileSize = 256;
65357 function tileSizeAtZoom(d, z) {
65358 var EPSILON = 0.002; // close seams
65360 return _tileSize * Math.pow(2, z - d[2]) / _tileSize + EPSILON;
65363 function atZoom(t, distance) {
65364 var power = Math.pow(2, distance);
65365 return [Math.floor(t[0] * power), Math.floor(t[1] * power), t[2] + distance];
65368 function lookUp(d) {
65369 for (var up = -1; up > -d[2]; up--) {
65370 var tile = atZoom(d, up);
65372 if (_cache[_source.url(tile)] !== false) {
65378 function uniqueBy(a, n) {
65382 for (var i = 0; i < a.length; i++) {
65383 if (seen[a[i][n]] === undefined) {
65385 seen[a[i][n]] = true;
65392 function addSource(d) {
65393 d.push(_source.url(d));
65395 } // Update tiles based on current state of `projection`.
65398 function background(selection) {
65399 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
65403 pixelOffset = [_source.offset()[0] * Math.pow(2, _zoom), _source.offset()[1] * Math.pow(2, _zoom)];
65405 pixelOffset = [0, 0];
65408 var translate = [_projection.translate()[0] + pixelOffset[0], _projection.translate()[1] + pixelOffset[1]];
65409 tiler.scale(_projection.scale() * 2 * Math.PI).translate(translate);
65410 _tileOrigin = [_projection.scale() * Math.PI - translate[0], _projection.scale() * Math.PI - translate[1]];
65412 } // Derive the tiles onscreen, remove those offscreen and position them.
65413 // Important that this part not depend on `_projection` because it's
65414 // rentered when tiles load/error (see #644).
65417 function render(selection) {
65418 if (!_source) return;
65420 var showDebug = context.getDebug('tile') && !_source.overlay;
65422 if (_source.validZoom(_zoom)) {
65423 tiler.skipNullIsland(!!_source.overlay);
65424 tiler().forEach(function (d) {
65426 if (d[3] === '') return;
65427 if (typeof d[3] !== 'string') return; // Workaround for #2295
65431 if (_cache[d[3]] === false && lookUp(d)) {
65432 requests.push(addSource(lookUp(d)));
65435 requests = uniqueBy(requests, 3).filter(function (r) {
65436 // don't re-request tiles which have failed in the past
65437 return _cache[r[3]] !== false;
65441 function load(d3_event, d) {
65442 _cache[d[3]] = true;
65443 select(this).on('error', null).on('load', null).classed('tile-loaded', true);
65447 function error(d3_event, d) {
65448 _cache[d[3]] = false;
65449 select(this).on('error', null).on('load', null).remove();
65453 function imageTransform(d) {
65454 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
65456 var scale = tileSizeAtZoom(d, _zoom);
65457 return 'translate(' + (d[0] * ts - _tileOrigin[0]) + 'px,' + (d[1] * ts - _tileOrigin[1]) + 'px) ' + 'scale(' + scale + ',' + scale + ')';
65460 function tileCenter(d) {
65461 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
65463 return [d[0] * ts - _tileOrigin[0] + ts / 2, d[1] * ts - _tileOrigin[1] + ts / 2];
65466 function debugTransform(d) {
65467 var coord = tileCenter(d);
65468 return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
65469 } // Pick a representative tile near the center of the viewport
65470 // (This is useful for sampling the imagery vintage)
65473 var dims = tiler.size();
65474 var mapCenter = [dims[0] / 2, dims[1] / 2];
65475 var minDist = Math.max(dims[0], dims[1]);
65477 requests.forEach(function (d) {
65478 var c = tileCenter(d);
65479 var dist = geoVecLength(c, mapCenter);
65481 if (dist < minDist) {
65486 var image = selection.selectAll('img').data(requests, function (d) {
65489 image.exit().style(transformProp, imageTransform).classed('tile-removing', true).classed('tile-center', false).each(function () {
65490 var tile = select(this);
65491 window.setTimeout(function () {
65492 if (tile.classed('tile-removing')) {
65497 image.enter().append('img').attr('class', 'tile').attr('draggable', 'false').style('width', _tileSize + 'px').style('height', _tileSize + 'px').attr('src', function (d) {
65499 }).on('error', error).on('load', load).merge(image).style(transformProp, imageTransform).classed('tile-debug', showDebug).classed('tile-removing', false).classed('tile-center', function (d) {
65500 return d === nearCenter;
65502 var debug = selection.selectAll('.tile-label-debug').data(showDebug ? requests : [], function (d) {
65505 debug.exit().remove();
65508 var debugEnter = debug.enter().append('div').attr('class', 'tile-label-debug');
65509 debugEnter.append('div').attr('class', 'tile-label-debug-coord');
65510 debugEnter.append('div').attr('class', 'tile-label-debug-vintage');
65511 debug = debug.merge(debugEnter);
65512 debug.style(transformProp, debugTransform);
65513 debug.selectAll('.tile-label-debug-coord').html(function (d) {
65514 return d[2] + ' / ' + d[0] + ' / ' + d[1];
65516 debug.selectAll('.tile-label-debug-vintage').each(function (d) {
65517 var span = select(this);
65518 var center = context.projection.invert(tileCenter(d));
65520 _source.getMetadata(center, d, function (err, result) {
65521 span.html(result && result.vintage && result.vintage.range || _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown'));
65527 background.projection = function (val) {
65528 if (!arguments.length) return _projection;
65533 background.dimensions = function (val) {
65534 if (!arguments.length) return tiler.size();
65539 background.source = function (val) {
65540 if (!arguments.length) return _source;
65542 _tileSize = _source.tileSize;
65544 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
65551 var _imageryIndex = null;
65552 function rendererBackground(context) {
65553 var dispatch$1 = dispatch('change');
65554 var detected = utilDetect();
65555 var baseLayer = rendererTileLayer(context).projection(context.projection);
65556 var _isValid = true;
65557 var _overlayLayers = [];
65558 var _brightness = 1;
65560 var _saturation = 1;
65561 var _sharpness = 1;
65563 function ensureImageryIndex() {
65564 return _mainFileFetcher.get('imagery').then(function (sources) {
65565 if (_imageryIndex) return _imageryIndex;
65569 }; // use which-polygon to support efficient index and querying for imagery
65571 var features = sources.map(function (source) {
65572 if (!source.polygon) return null; // workaround for editor-layer-index weirdness..
65573 // Add an extra array nest to each element in `source.polygon`
65574 // so the rings are not treated as a bunch of holes:
65575 // what we have: [ [[outer],[hole],[hole]] ]
65576 // what we want: [ [[outer]],[[outer]],[[outer]] ]
65578 var rings = source.polygon.map(function (ring) {
65587 type: 'MultiPolygon',
65591 _imageryIndex.features[source.id] = feature;
65593 }).filter(Boolean);
65594 _imageryIndex.query = whichPolygon_1({
65595 type: 'FeatureCollection',
65597 }); // Instantiate `rendererBackgroundSource` objects for each source
65599 _imageryIndex.backgrounds = sources.map(function (source) {
65600 if (source.type === 'bing') {
65601 return rendererBackgroundSource.Bing(source, dispatch$1);
65602 } else if (/^EsriWorldImagery/.test(source.id)) {
65603 return rendererBackgroundSource.Esri(source);
65605 return rendererBackgroundSource(source);
65609 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); // Add 'Custom'
65612 var template = corePreferences('background-custom-template') || '';
65613 var custom = rendererBackgroundSource.Custom(template);
65615 _imageryIndex.backgrounds.unshift(custom);
65617 return _imageryIndex;
65621 function background(selection) {
65622 var currSource = baseLayer.source(); // If we are displaying an Esri basemap at high zoom,
65623 // check its tilemap to see how high the zoom can go
65625 if (context.map().zoom() > 18) {
65626 if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
65627 var center = context.map().center();
65628 currSource.fetchTilemap(center);
65630 } // Is the imagery valid here? - #4827
65633 var sources = background.sources(context.map().extent());
65634 var wasValid = _isValid;
65635 _isValid = !!sources.filter(function (d) {
65636 return d === currSource;
65639 if (wasValid !== _isValid) {
65640 // change in valid status
65641 background.updateImagery();
65644 var baseFilter = '';
65646 if (detected.cssfilters) {
65647 if (_brightness !== 1) {
65648 baseFilter += " brightness(".concat(_brightness, ")");
65651 if (_contrast !== 1) {
65652 baseFilter += " contrast(".concat(_contrast, ")");
65655 if (_saturation !== 1) {
65656 baseFilter += " saturate(".concat(_saturation, ")");
65659 if (_sharpness < 1) {
65661 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
65662 baseFilter += " blur(".concat(blur, "px)");
65666 var base = selection.selectAll('.layer-background').data([0]);
65667 base = base.enter().insert('div', '.layer-data').attr('class', 'layer layer-background').merge(base);
65669 if (detected.cssfilters) {
65670 base.style('filter', baseFilter || null);
65672 base.style('opacity', _brightness);
65675 var imagery = base.selectAll('.layer-imagery').data([0]);
65676 imagery.enter().append('div').attr('class', 'layer layer-imagery').merge(imagery).call(baseLayer);
65677 var maskFilter = '';
65678 var mixBlendMode = '';
65680 if (detected.cssfilters && _sharpness > 1) {
65681 // apply unsharp mask
65682 mixBlendMode = 'overlay';
65683 maskFilter = 'saturate(0) blur(3px) invert(1)';
65684 var contrast = _sharpness - 1;
65685 maskFilter += " contrast(".concat(contrast, ")");
65686 var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
65687 maskFilter += " brightness(".concat(brightness, ")");
65690 var mask = base.selectAll('.layer-unsharp-mask').data(detected.cssfilters && _sharpness > 1 ? [0] : []);
65691 mask.exit().remove();
65692 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);
65693 var overlays = selection.selectAll('.layer-overlay').data(_overlayLayers, function (d) {
65694 return d.source().name();
65696 overlays.exit().remove();
65697 overlays.enter().insert('div', '.layer-data').attr('class', 'layer layer-overlay').merge(overlays).each(function (layer, i, nodes) {
65698 return select(nodes[i]).call(layer);
65702 background.updateImagery = function () {
65703 var currSource = baseLayer.source();
65704 if (context.inIntro() || !currSource) return;
65706 var o = _overlayLayers.filter(function (d) {
65707 return !d.source().isLocatorOverlay() && !d.source().isHidden();
65708 }).map(function (d) {
65709 return d.source().id;
65712 var meters = geoOffsetToMeters(currSource.offset());
65713 var EPSILON = 0.01;
65714 var x = +meters[0].toFixed(2);
65715 var y = +meters[1].toFixed(2);
65716 var hash = utilStringQs(window.location.hash);
65717 var id = currSource.id;
65719 if (id === 'custom') {
65720 id = "custom:".concat(currSource.template());
65724 hash.background = id;
65726 delete hash.background;
65732 delete hash.overlays;
65735 if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
65736 hash.offset = "".concat(x, ",").concat(y);
65738 delete hash.offset;
65741 if (!window.mocha) {
65742 window.location.replace('#' + utilQsString(hash, true));
65745 var imageryUsed = [];
65746 var photoOverlaysUsed = [];
65747 var currUsed = currSource.imageryUsed();
65749 if (currUsed && _isValid) {
65750 imageryUsed.push(currUsed);
65753 _overlayLayers.filter(function (d) {
65754 return !d.source().isLocatorOverlay() && !d.source().isHidden();
65755 }).forEach(function (d) {
65756 return imageryUsed.push(d.source().imageryUsed());
65759 var dataLayer = context.layers().layer('data');
65761 if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
65762 imageryUsed.push(dataLayer.getSrc());
65765 var photoOverlayLayers = {
65766 streetside: 'Bing Streetside',
65767 mapillary: 'Mapillary Images',
65768 'mapillary-map-features': 'Mapillary Map Features',
65769 'mapillary-signs': 'Mapillary Signs',
65770 openstreetcam: 'OpenStreetCam Images'
65773 for (var layerID in photoOverlayLayers) {
65774 var layer = context.layers().layer(layerID);
65776 if (layer && layer.enabled()) {
65777 photoOverlaysUsed.push(layerID);
65778 imageryUsed.push(photoOverlayLayers[layerID]);
65782 context.history().imageryUsed(imageryUsed);
65783 context.history().photoOverlaysUsed(photoOverlaysUsed);
65786 var _checkedBlocklists;
65788 background.sources = function (extent, zoom, includeCurrent) {
65789 if (!_imageryIndex) return []; // called before init()?
65792 (_imageryIndex.query.bbox(extent.rectangle(), true) || []).forEach(function (d) {
65793 return visible[d.id] = true;
65795 var currSource = baseLayer.source();
65796 var osm = context.connection();
65797 var blocklists = osm && osm.imageryBlocklists();
65799 if (blocklists && blocklists !== _checkedBlocklists) {
65800 _imageryIndex.backgrounds.forEach(function (source) {
65801 source.isBlocked = blocklists.some(function (blocklist) {
65802 return blocklist.test(source.template());
65806 _checkedBlocklists = blocklists;
65809 return _imageryIndex.backgrounds.filter(function (source) {
65810 if (includeCurrent && currSource === source) return true; // optionally always include the current imagery
65812 if (source.isBlocked) return false; // even bundled sources may be blocked - #7905
65814 if (!source.polygon) return true; // always include imagery with worldwide coverage
65816 if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms
65818 return visible[source.id]; // include imagery visible in given extent
65822 background.dimensions = function (val) {
65824 baseLayer.dimensions(val);
65826 _overlayLayers.forEach(function (layer) {
65827 return layer.dimensions(val);
65831 background.baseLayerSource = function (d) {
65832 if (!arguments.length) return baseLayer.source(); // test source against OSM imagery blocklists..
65834 var osm = context.connection();
65835 if (!osm) return background;
65836 var blocklists = osm.imageryBlocklists();
65837 var template = d.template();
65842 for (var i = 0; i < blocklists.length; i++) {
65843 regex = blocklists[i];
65844 fail = regex.test(template);
65847 } // ensure at least one test was run.
65851 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
65852 fail = regex.test(template);
65855 baseLayer.source(!fail ? d : background.findSource('none'));
65856 dispatch$1.call('change');
65857 background.updateImagery();
65861 background.findSource = function (id) {
65862 if (!id || !_imageryIndex) return null; // called before init()?
65864 return _imageryIndex.backgrounds.find(function (d) {
65865 return d.id && d.id === id;
65869 background.bing = function () {
65870 background.baseLayerSource(background.findSource('Bing'));
65873 background.showsLayer = function (d) {
65874 var currSource = baseLayer.source();
65875 if (!d || !currSource) return false;
65876 return d.id === currSource.id || _overlayLayers.some(function (layer) {
65877 return d.id === layer.source().id;
65881 background.overlayLayerSources = function () {
65882 return _overlayLayers.map(function (layer) {
65883 return layer.source();
65887 background.toggleOverlayLayer = function (d) {
65890 for (var i = 0; i < _overlayLayers.length; i++) {
65891 layer = _overlayLayers[i];
65893 if (layer.source() === d) {
65894 _overlayLayers.splice(i, 1);
65896 dispatch$1.call('change');
65897 background.updateImagery();
65902 layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions());
65904 _overlayLayers.push(layer);
65906 dispatch$1.call('change');
65907 background.updateImagery();
65910 background.nudge = function (d, zoom) {
65911 var currSource = baseLayer.source();
65914 currSource.nudge(d, zoom);
65915 dispatch$1.call('change');
65916 background.updateImagery();
65922 background.offset = function (d) {
65923 var currSource = baseLayer.source();
65925 if (!arguments.length) {
65926 return currSource && currSource.offset() || [0, 0];
65930 currSource.offset(d);
65931 dispatch$1.call('change');
65932 background.updateImagery();
65938 background.brightness = function (d) {
65939 if (!arguments.length) return _brightness;
65941 if (context.mode()) dispatch$1.call('change');
65945 background.contrast = function (d) {
65946 if (!arguments.length) return _contrast;
65948 if (context.mode()) dispatch$1.call('change');
65952 background.saturation = function (d) {
65953 if (!arguments.length) return _saturation;
65955 if (context.mode()) dispatch$1.call('change');
65959 background.sharpness = function (d) {
65960 if (!arguments.length) return _sharpness;
65962 if (context.mode()) dispatch$1.call('change');
65968 background.ensureLoaded = function () {
65969 if (_loadPromise) return _loadPromise;
65971 function parseMapParams(qmap) {
65972 if (!qmap) return false;
65973 var params = qmap.split('/').map(Number);
65974 if (params.length < 3 || params.some(isNaN)) return false;
65975 return geoExtent([params[2], params[1]]); // lon,lat
65978 var hash = utilStringQs(window.location.hash);
65979 var requested = hash.background || hash.layer;
65980 var extent = parseMapParams(hash.map);
65981 return _loadPromise = ensureImageryIndex().then(function (imageryIndex) {
65982 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
65985 if (!requested && extent) {
65986 best = background.sources(extent).find(function (s) {
65989 } // Decide which background layer to display
65992 if (requested && requested.indexOf('custom:') === 0) {
65993 var template = requested.replace(/^custom:/, '');
65994 var custom = background.findSource('custom');
65995 background.baseLayerSource(custom.template(template));
65996 corePreferences('background-custom-template', template);
65998 background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences('background-last-used')) || background.findSource('Bing') || first || background.findSource('none'));
66001 var locator = imageryIndex.backgrounds.find(function (d) {
66002 return d.overlay && d["default"];
66006 background.toggleOverlayLayer(locator);
66009 var overlays = (hash.overlays || '').split(',');
66010 overlays.forEach(function (overlay) {
66011 overlay = background.findSource(overlay);
66014 background.toggleOverlayLayer(overlay);
66019 var gpx = context.layers().layer('data');
66022 gpx.url(hash.gpx, '.gpx');
66027 var offset = hash.offset.replace(/;/g, ',').split(',').map(function (n) {
66028 return !isNaN(n) && n;
66031 if (offset.length === 2) {
66032 background.offset(geoMetersToOffset(offset));
66035 })["catch"](function () {
66040 return utilRebind(background, dispatch$1, 'on');
66043 function rendererFeatures(context) {
66044 var dispatch$1 = dispatch('change', 'redraw');
66045 var features = utilRebind({}, dispatch$1, 'on');
66047 var _deferred = new Set();
66049 var traffic_roads = {
66051 'motorway_link': true,
66053 'trunk_link': true,
66055 'primary_link': true,
66057 'secondary_link': true,
66059 'tertiary_link': true,
66060 'residential': true,
66061 'unclassified': true,
66062 'living_street': true
66064 var service_roads = {
66077 var past_futures = {
66079 'construction': true,
66081 'dismantled': true,
66084 'demolished': true,
66085 'obliterated': true
66087 var _cullFactor = 1;
66093 var _forceVisible = {};
66095 function update() {
66096 if (!window.mocha) {
66097 var hash = utilStringQs(window.location.hash);
66098 var disabled = features.disabled();
66100 if (disabled.length) {
66101 hash.disable_features = disabled.join(',');
66103 delete hash.disable_features;
66106 window.location.replace('#' + utilQsString(hash, true));
66107 corePreferences('disabled-features', disabled.join(','));
66110 _hidden = features.hidden();
66111 dispatch$1.call('change');
66112 dispatch$1.call('redraw');
66115 function defineRule(k, filter, max) {
66116 var isEnabled = true;
66122 enabled: isEnabled,
66123 // whether the user wants it enabled..
66125 currentMax: max || Infinity,
66126 defaultMax: max || Infinity,
66127 enable: function enable() {
66128 this.enabled = true;
66129 this.currentMax = this.defaultMax;
66131 disable: function disable() {
66132 this.enabled = false;
66133 this.currentMax = 0;
66135 hidden: function hidden() {
66136 return this.count === 0 && !this.enabled || this.count > this.currentMax * _cullFactor;
66138 autoHidden: function autoHidden() {
66139 return this.hidden() && this.currentMax > 0;
66144 defineRule('points', function isPoint(tags, geometry) {
66145 return geometry === 'point';
66147 defineRule('traffic_roads', function isTrafficRoad(tags) {
66148 return traffic_roads[tags.highway];
66150 defineRule('service_roads', function isServiceRoad(tags) {
66151 return service_roads[tags.highway];
66153 defineRule('paths', function isPath(tags) {
66154 return paths[tags.highway];
66156 defineRule('buildings', function isBuilding(tags) {
66157 return !!tags.building && tags.building !== 'no' || tags.parking === 'multi-storey' || tags.parking === 'sheds' || tags.parking === 'carports' || tags.parking === 'garage_boxes';
66159 defineRule('building_parts', function isBuildingPart(tags) {
66160 return tags['building:part'];
66162 defineRule('indoor', function isIndoor(tags) {
66163 return tags.indoor;
66165 defineRule('landuse', function isLanduse(tags, geometry) {
66166 return geometry === 'area' && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags);
66168 defineRule('boundaries', function isBoundary(tags) {
66169 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);
66171 defineRule('water', function isWater(tags) {
66172 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';
66174 defineRule('rail', function isRail(tags) {
66175 return (!!tags.railway || tags.landuse === 'railway') && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]);
66177 defineRule('pistes', function isPiste(tags) {
66178 return tags['piste:type'];
66180 defineRule('aerialways', function isPiste(tags) {
66181 return tags.aerialway && tags.aerialway !== 'yes' && tags.aerialway !== 'station';
66183 defineRule('power', function isPower(tags) {
66184 return !!tags.power;
66185 }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
66187 defineRule('past_future', function isPastFuture(tags) {
66188 if (traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]) {
66192 var strings = Object.keys(tags);
66194 for (var i = 0; i < strings.length; i++) {
66195 var s = strings[i];
66197 if (past_futures[s] || past_futures[tags[s]]) {
66203 }); // Lines or areas that don't match another feature filter.
66204 // IMPORTANT: The 'others' feature must be the last one defined,
66205 // so that code in getMatches can skip this test if `hasMatch = true`
66207 defineRule('others', function isOther(tags, geometry) {
66208 return geometry === 'line' || geometry === 'area';
66211 features.features = function () {
66215 features.keys = function () {
66219 features.enabled = function (k) {
66220 if (!arguments.length) {
66221 return _keys.filter(function (k) {
66222 return _rules[k].enabled;
66226 return _rules[k] && _rules[k].enabled;
66229 features.disabled = function (k) {
66230 if (!arguments.length) {
66231 return _keys.filter(function (k) {
66232 return !_rules[k].enabled;
66236 return _rules[k] && !_rules[k].enabled;
66239 features.hidden = function (k) {
66240 if (!arguments.length) {
66241 return _keys.filter(function (k) {
66242 return _rules[k].hidden();
66246 return _rules[k] && _rules[k].hidden();
66249 features.autoHidden = function (k) {
66250 if (!arguments.length) {
66251 return _keys.filter(function (k) {
66252 return _rules[k].autoHidden();
66256 return _rules[k] && _rules[k].autoHidden();
66259 features.enable = function (k) {
66260 if (_rules[k] && !_rules[k].enabled) {
66261 _rules[k].enable();
66267 features.enableAll = function () {
66268 var didEnable = false;
66270 for (var k in _rules) {
66271 if (!_rules[k].enabled) {
66274 _rules[k].enable();
66278 if (didEnable) update();
66281 features.disable = function (k) {
66282 if (_rules[k] && _rules[k].enabled) {
66283 _rules[k].disable();
66289 features.disableAll = function () {
66290 var didDisable = false;
66292 for (var k in _rules) {
66293 if (_rules[k].enabled) {
66296 _rules[k].disable();
66300 if (didDisable) update();
66303 features.toggle = function (k) {
66306 return f.enabled ? f.disable() : f.enable();
66313 features.resetStats = function () {
66314 for (var i = 0; i < _keys.length; i++) {
66315 _rules[_keys[i]].count = 0;
66318 dispatch$1.call('change');
66321 features.gatherStats = function (d, resolver, dimensions) {
66322 var needsRedraw = false;
66323 var types = utilArrayGroupBy(d, 'type');
66324 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
66325 var currHidden, geometry, matches, i, j;
66327 for (i = 0; i < _keys.length; i++) {
66328 _rules[_keys[i]].count = 0;
66329 } // adjust the threshold for point/building culling based on viewport size..
66330 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
66333 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
66335 for (i = 0; i < entities.length; i++) {
66336 geometry = entities[i].geometry(resolver);
66337 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
66339 for (j = 0; j < matches.length; j++) {
66340 _rules[matches[j]].count++;
66344 currHidden = features.hidden();
66346 if (currHidden !== _hidden) {
66347 _hidden = currHidden;
66348 needsRedraw = true;
66349 dispatch$1.call('change');
66352 return needsRedraw;
66355 features.stats = function () {
66356 for (var i = 0; i < _keys.length; i++) {
66357 _stats[_keys[i]] = _rules[_keys[i]].count;
66363 features.clear = function (d) {
66364 for (var i = 0; i < d.length; i++) {
66365 features.clearEntity(d[i]);
66369 features.clearEntity = function (entity) {
66370 delete _cache[osmEntity.key(entity)];
66373 features.reset = function () {
66374 Array.from(_deferred).forEach(function (handle) {
66375 window.cancelIdleCallback(handle);
66377 _deferred["delete"](handle);
66380 }; // only certain relations are worth checking
66383 function relationShouldBeChecked(relation) {
66384 // multipolygon features have `area` geometry and aren't checked here
66385 return relation.tags.type === 'boundary';
66388 features.getMatches = function (entity, resolver, geometry) {
66389 if (geometry === 'vertex' || geometry === 'relation' && !relationShouldBeChecked(entity)) return {};
66390 var ent = osmEntity.key(entity);
66392 if (!_cache[ent]) {
66396 if (!_cache[ent].matches) {
66398 var hasMatch = false;
66400 for (var i = 0; i < _keys.length; i++) {
66401 if (_keys[i] === 'others') {
66402 if (hasMatch) continue; // If an entity...
66403 // 1. is a way that hasn't matched other 'interesting' feature rules,
66405 if (entity.type === 'way') {
66406 var parents = features.getParents(entity, resolver, geometry); // 2a. belongs only to a single multipolygon relation
66408 if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations
66409 parents.length > 0 && parents.every(function (parent) {
66410 return parent.tags.type === 'boundary';
66412 // ...then match whatever feature rules the parent relation has matched.
66413 // see #2548, #2887
66416 // For this to work, getMatches must be called on relations before ways.
66418 var pkey = osmEntity.key(parents[0]);
66420 if (_cache[pkey] && _cache[pkey].matches) {
66421 matches = Object.assign({}, _cache[pkey].matches); // shallow copy
66429 if (_rules[_keys[i]].filter(entity.tags, geometry)) {
66430 matches[_keys[i]] = hasMatch = true;
66434 _cache[ent].matches = matches;
66437 return _cache[ent].matches;
66440 features.getParents = function (entity, resolver, geometry) {
66441 if (geometry === 'point') return [];
66442 var ent = osmEntity.key(entity);
66444 if (!_cache[ent]) {
66448 if (!_cache[ent].parents) {
66451 if (geometry === 'vertex') {
66452 parents = resolver.parentWays(entity);
66454 // 'line', 'area', 'relation'
66455 parents = resolver.parentRelations(entity);
66458 _cache[ent].parents = parents;
66461 return _cache[ent].parents;
66464 features.isHiddenPreset = function (preset, geometry) {
66465 if (!_hidden.length) return false;
66466 if (!preset.tags) return false;
66467 var test = preset.setTags({}, geometry);
66469 for (var key in _rules) {
66470 if (_rules[key].filter(test, geometry)) {
66471 if (_hidden.indexOf(key) !== -1) {
66482 features.isHiddenFeature = function (entity, resolver, geometry) {
66483 if (!_hidden.length) return false;
66484 if (!entity.version) return false;
66485 if (_forceVisible[entity.id]) return false;
66486 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
66487 return matches.length && matches.every(function (k) {
66488 return features.hidden(k);
66492 features.isHiddenChild = function (entity, resolver, geometry) {
66493 if (!_hidden.length) return false;
66494 if (!entity.version || geometry === 'point') return false;
66495 if (_forceVisible[entity.id]) return false;
66496 var parents = features.getParents(entity, resolver, geometry);
66497 if (!parents.length) return false;
66499 for (var i = 0; i < parents.length; i++) {
66500 if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
66508 features.hasHiddenConnections = function (entity, resolver) {
66509 if (!_hidden.length) return false;
66510 var childNodes, connections;
66512 if (entity.type === 'midpoint') {
66513 childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
66516 childNodes = entity.nodes ? resolver.childNodes(entity) : [];
66517 connections = features.getParents(entity, resolver, entity.geometry(resolver));
66518 } // gather ways connected to child nodes..
66521 connections = childNodes.reduce(function (result, e) {
66522 return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
66524 return connections.some(function (e) {
66525 return features.isHidden(e, resolver, e.geometry(resolver));
66529 features.isHidden = function (entity, resolver, geometry) {
66530 if (!_hidden.length) return false;
66531 if (!entity.version) return false;
66532 var fn = geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature;
66533 return fn(entity, resolver, geometry);
66536 features.filter = function (d, resolver) {
66537 if (!_hidden.length) return d;
66540 for (var i = 0; i < d.length; i++) {
66543 if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
66544 result.push(entity);
66551 features.forceVisible = function (entityIDs) {
66552 if (!arguments.length) return Object.keys(_forceVisible);
66553 _forceVisible = {};
66555 for (var i = 0; i < entityIDs.length; i++) {
66556 _forceVisible[entityIDs[i]] = true;
66557 var entity = context.hasEntity(entityIDs[i]);
66559 if (entity && entity.type === 'relation') {
66560 // also show relation members (one level deep)
66561 for (var j in entity.members) {
66562 _forceVisible[entity.members[j].id] = true;
66570 features.init = function () {
66571 var storage = corePreferences('disabled-features');
66574 var storageDisabled = storage.replace(/;/g, ',').split(',');
66575 storageDisabled.forEach(features.disable);
66578 var hash = utilStringQs(window.location.hash);
66580 if (hash.disable_features) {
66581 var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
66582 hashDisabled.forEach(features.disable);
66584 }; // warm up the feature matching cache upon merging fetched data
66587 context.history().on('merge.features', function (newEntities) {
66588 if (!newEntities) return;
66589 var handle = window.requestIdleCallback(function () {
66590 var graph = context.graph();
66591 var types = utilArrayGroupBy(newEntities, 'type'); // ensure that getMatches is called on relations before ways
66593 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
66595 for (var i = 0; i < entities.length; i++) {
66596 var geometry = entities[i].geometry(graph);
66597 features.getMatches(entities[i], graph, geometry);
66601 _deferred.add(handle);
66607 // - the activeID - nope
66608 // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
66609 // - 2 away from the activeID - nope (would create a self intersecting segment)
66610 // - all others on a linear way - yes
66611 // - all others on a closed way - nope (would create a self intersecting polygon)
66614 // 0 = active vertex - no touch/connect
66615 // 1 = passive vertex - yes touch/connect
66616 // 2 = adjacent vertex - yes but pay attention segmenting a line here
66619 function svgPassiveVertex(node, graph, activeID) {
66620 if (!activeID) return 1;
66621 if (activeID === node.id) return 0;
66622 var parents = graph.parentWays(node);
66623 var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
66625 for (i = 0; i < parents.length; i++) {
66626 nodes = parents[i].nodes;
66627 isClosed = parents[i].isClosed();
66629 for (j = 0; j < nodes.length; j++) {
66630 // find this vertex, look nearby
66631 if (nodes[j] === node.id) {
66638 // wraparound if needed
66639 max = nodes.length - 1;
66640 if (ix1 < 0) ix1 = max + ix1;
66641 if (ix2 < 0) ix2 = max + ix2;
66642 if (ix3 > max) ix3 = ix3 - max;
66643 if (ix4 > max) ix4 = ix4 - max;
66646 if (nodes[ix1] === activeID) return 0; // no - prevent self intersect
66647 else if (nodes[ix2] === activeID) return 2; // ok - adjacent
66648 else if (nodes[ix3] === activeID) return 2; // ok - adjacent
66649 else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect
66650 else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect
66657 function svgMarkerSegments(projection, graph, dt, shouldReverse, bothDirections) {
66658 return function (entity) {
66662 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
66663 var coordinates = graph.childNodes(entity).map(function (n) {
66668 if (shouldReverse(entity)) {
66669 coordinates.reverse();
66673 type: 'LineString',
66674 coordinates: coordinates
66675 }, projection.stream(clip({
66676 lineStart: function lineStart() {},
66677 lineEnd: function lineEnd() {
66680 point: function point(x, y) {
66684 var span = geoVecLength(a, b) - offset;
66687 var heading = geoVecAngle(a, b);
66688 var dx = dt * Math.cos(heading);
66689 var dy = dt * Math.sin(heading);
66690 var p = [a[0] + offset * Math.cos(heading), a[1] + offset * Math.sin(heading)]; // gather coordinates
66692 var coord = [a, p];
66694 for (span -= dt; span >= 0; span -= dt) {
66695 p = geoVecAdd(p, [dx, dy]);
66699 coord.push(b); // generate svg paths
66704 for (j = 0; j < coord.length; j++) {
66705 segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
66714 if (bothDirections(entity)) {
66717 for (j = coord.length - 1; j >= 0; j--) {
66718 segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
66738 function svgPath(projection, graph, isArea) {
66739 // Explanation of magic numbers:
66740 // "padding" here allows space for strokes to extend beyond the viewport,
66741 // so that the stroke isn't drawn along the edge of the viewport when
66742 // the shape is clipped.
66744 // When drawing lines, pad viewport by 5px.
66745 // When drawing areas, pad viewport by 65px in each direction to allow
66746 // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
66748 var padding = isArea ? 65 : 5;
66749 var viewport = projection.clipExtent();
66750 var paddedExtent = [[viewport[0][0] - padding, viewport[0][1] - padding], [viewport[1][0] + padding, viewport[1][1] + padding]];
66751 var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
66752 var project = projection.stream;
66753 var path = d3_geoPath().projection({
66754 stream: function stream(output) {
66755 return project(clip(output));
66759 var svgpath = function svgpath(entity) {
66760 if (entity.id in cache) {
66761 return cache[entity.id];
66763 return cache[entity.id] = path(entity.asGeoJSON(graph));
66767 svgpath.geojson = function (d) {
66768 if (d.__featurehash__ !== undefined) {
66769 if (d.__featurehash__ in cache) {
66770 return cache[d.__featurehash__];
66772 return cache[d.__featurehash__] = path(d);
66781 function svgPointTransform(projection) {
66782 var svgpoint = function svgpoint(entity) {
66783 // http://jsperf.com/short-array-join
66784 var pt = projection(entity.loc);
66785 return 'translate(' + pt[0] + ',' + pt[1] + ')';
66788 svgpoint.geojson = function (d) {
66789 return svgpoint(d.properties.entity);
66794 function svgRelationMemberTags(graph) {
66795 return function (entity) {
66796 var tags = entity.tags;
66797 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
66798 graph.parentRelations(entity).forEach(function (relation) {
66799 var type = relation.tags.type;
66801 if (type === 'multipolygon' && shouldCopyMultipolygonTags || type === 'boundary') {
66802 tags = Object.assign({}, relation.tags, tags);
66808 function svgSegmentWay(way, graph, activeID) {
66809 // When there is no activeID, we can memoize this expensive computation
66810 if (activeID === undefined) {
66811 return graph["transient"](way, 'waySegments', getWaySegments);
66813 return getWaySegments();
66816 function getWaySegments() {
66817 var isActiveWay = way.nodes.indexOf(activeID) !== -1;
66826 for (var i = 0; i < way.nodes.length; i++) {
66827 node = graph.entity(way.nodes[i]);
66828 type = svgPassiveVertex(node, graph, activeID);
66834 if (start.type !== undefined) {
66835 if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {
66836 // one adjacent vertex
66837 pushActive(start, end, i);
66838 } else if (start.type === 0 && end.type === 0) {
66839 // both active vertices
66840 pushActive(start, end, i);
66842 pushPassive(start, end, i);
66851 function pushActive(start, end, index) {
66852 features.active.push({
66854 id: way.id + '-' + index + '-nope',
66859 nodes: [start.node, end.node],
66863 type: 'LineString',
66864 coordinates: [start.node.loc, end.node.loc]
66869 function pushPassive(start, end, index) {
66870 features.passive.push({
66872 id: way.id + '-' + index,
66876 nodes: [start.node, end.node],
66880 type: 'LineString',
66881 coordinates: [start.node.loc, end.node.loc]
66888 function svgTagClasses() {
66889 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'];
66890 var statuses = [// nonexistent, might be built
66891 'proposed', 'planned', // under maintentance or between groundbreaking and opening
66892 'construction', // existent but not functional
66893 'disused', // dilapidated to nonexistent
66894 'abandoned', // nonexistent, still may appear in imagery
66895 'dismantled', 'razed', 'demolished', 'obliterated', // existent occasionally, e.g. stormwater drainage basin
66897 var secondaries = ['oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', 'man_made', 'indoor'];
66899 var _tags = function _tags(entity) {
66900 return entity.tags;
66903 var tagClasses = function tagClasses(selection) {
66904 selection.each(function tagClassesEach(entity) {
66905 var value = this.className;
66907 if (value.baseVal !== undefined) {
66908 value = value.baseVal;
66911 var t = _tags(entity);
66913 var computed = tagClasses.getClassesString(t, value);
66915 if (computed !== value) {
66916 select(this).attr('class', computed);
66921 tagClasses.getClassesString = function (t, value) {
66922 var primary, status;
66923 var i, j, k, v; // in some situations we want to render perimeter strokes a certain way
66925 var overrideGeometry;
66927 if (/\bstroke\b/.test(value)) {
66928 if (!!t.barrier && t.barrier !== 'no') {
66929 overrideGeometry = 'line';
66931 } // preserve base classes (nothing with `tag-`)
66934 var classes = value.trim().split(/\s+/).filter(function (klass) {
66935 return klass.length && !/^tag-/.test(klass);
66936 }).map(function (klass) {
66937 // special overrides for some perimeter strokes
66938 return klass === 'line' || klass === 'area' ? overrideGeometry || klass : klass;
66939 }); // pick at most one primary classification tag..
66941 for (i = 0; i < primaries.length; i++) {
66944 if (!v || v === 'no') continue;
66946 if (k === 'piste:type') {
66947 // avoid a ':' in the class name
66949 } else if (k === 'building:part') {
66950 // avoid a ':' in the class name
66951 k = 'building_part';
66956 if (statuses.indexOf(v) !== -1) {
66957 // e.g. `railway=abandoned`
66959 classes.push('tag-' + k);
66961 classes.push('tag-' + k);
66962 classes.push('tag-' + k + '-' + v);
66969 for (i = 0; i < statuses.length; i++) {
66970 for (j = 0; j < primaries.length; j++) {
66971 k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes`
66974 if (!v || v === 'no') continue;
66975 status = statuses[i];
66979 } // add at most one status tag, only if relates to primary tag..
66983 for (i = 0; i < statuses.length; i++) {
66986 if (!v || v === 'no') continue;
66989 // e.g. `railway=rail + abandoned=yes`
66991 } else if (primary && primary === v) {
66992 // e.g. `railway=rail + abandoned=railway`
66994 } else if (!primary && primaries.indexOf(v) !== -1) {
66995 // e.g. `abandoned=railway`
66998 classes.push('tag-' + v);
66999 } // else ignore e.g. `highway=path + abandoned=railway`
67007 classes.push('tag-status');
67008 classes.push('tag-status-' + status);
67009 } // add any secondary tags
67012 for (i = 0; i < secondaries.length; i++) {
67013 k = secondaries[i];
67015 if (!v || v === 'no' || k === primary) continue;
67016 classes.push('tag-' + k);
67017 classes.push('tag-' + k + '-' + v);
67018 } // For highways, look for surface tagging..
67021 if (primary === 'highway' && !osmPathHighwayTagValues[t.highway] || primary === 'aeroway') {
67022 var surface = t.highway === 'track' ? 'unpaved' : 'paved';
67027 if (k in osmPavedTags) {
67028 surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
67031 if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
67032 surface = 'semipaved';
67036 classes.push('tag-' + surface);
67037 } // If this is a wikidata-tagged item, add a class for that..
67040 if (t.wikidata || t['brand:wikidata']) {
67041 classes.push('tag-wikidata');
67044 return classes.join(' ').trim();
67047 tagClasses.tags = function (val) {
67048 if (!arguments.length) return _tags;
67056 // Patterns only work in Firefox when set directly on element.
67057 // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
67059 // tag - pattern name
67061 // tag - value - pattern name
67063 // tag - value - rules (optional tag-values, pattern name)
67064 // (matches earlier rules first, so fallback should be last entry)
67066 grave_yard: 'cemetery',
67067 fountain: 'water_standing'
67071 religion: 'christian',
67072 pattern: 'cemetery_christian'
67074 religion: 'buddhist',
67075 pattern: 'cemetery_buddhist'
67077 religion: 'muslim',
67078 pattern: 'cemetery_muslim'
67080 religion: 'jewish',
67081 pattern: 'cemetery_jewish'
67083 pattern: 'cemetery'
67085 construction: 'construction',
67086 farmland: 'farmland',
67087 farmyard: 'farmyard',
67089 leaf_type: 'broadleaved',
67090 pattern: 'forest_broadleaved'
67092 leaf_type: 'needleleaved',
67093 pattern: 'forest_needleleaved'
67095 leaf_type: 'leafless',
67096 pattern: 'forest_leafless'
67099 } // same as 'leaf_type:mixed'
67101 grave_yard: 'cemetery',
67104 pattern: 'golf_green'
67108 landfill: 'landfill',
67110 military: 'construction',
67111 orchard: 'orchard',
67113 vineyard: 'vineyard'
67117 grassland: 'grass',
67124 water: 'reservoir',
67125 pattern: 'water_standing'
67131 pattern: 'wetland_marsh'
67134 pattern: 'wetland_swamp'
67137 pattern: 'wetland_bog'
67139 wetland: 'reedbed',
67140 pattern: 'wetland_reedbed'
67145 leaf_type: 'broadleaved',
67146 pattern: 'forest_broadleaved'
67148 leaf_type: 'needleleaved',
67149 pattern: 'forest_needleleaved'
67151 leaf_type: 'leafless',
67152 pattern: 'forest_leafless'
67155 } // same as 'leaf_type:mixed'
67173 function svgTagPattern(tags) {
67174 // Skip pattern filling if this is a building (buildings don't get patterns applied)
67175 if (tags.building && tags.building !== 'no') {
67179 for (var tag in patterns) {
67180 var entityValue = tags[tag];
67181 if (!entityValue) continue;
67183 if (typeof patterns[tag] === 'string') {
67184 // extra short syntax (just tag) - pattern name
67185 return 'pattern-' + patterns[tag];
67187 var values = patterns[tag];
67189 for (var value in values) {
67190 if (entityValue !== value) continue;
67191 var rules = values[value];
67193 if (typeof rules === 'string') {
67194 // short syntax - pattern name
67195 return 'pattern-' + rules;
67196 } // long syntax - rule array
67199 for (var ruleKey in rules) {
67200 var rule = rules[ruleKey];
67203 for (var criterion in rule) {
67204 if (criterion !== 'pattern') {
67205 // reserved for pattern name
67206 // The only rule is a required tag-value pair
67207 var v = tags[criterion];
67209 if (!v || v !== rule[criterion]) {
67217 return 'pattern-' + rule.pattern;
67227 function svgAreas(projection, context) {
67228 function getPatternStyle(tags) {
67229 var imageID = svgTagPattern(tags);
67232 return 'url("#ideditor-' + imageID + '")';
67238 function drawTargets(selection, graph, entities, filter) {
67239 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
67240 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
67241 var getPath = svgPath(projection).geojson;
67242 var activeID = context.activeID();
67243 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
67249 entities.forEach(function (way) {
67250 var features = svgSegmentWay(way, graph, activeID);
67251 data.targets.push.apply(data.targets, features.passive);
67252 data.nopes.push.apply(data.nopes, features.active);
67253 }); // Targets allow hover and vertex snapping
67255 var targetData = data.targets.filter(getPath);
67256 var targets = selection.selectAll('.area.target-allowed').filter(function (d) {
67257 return filter(d.properties.entity);
67258 }).data(targetData, function key(d) {
67262 targets.exit().remove();
67264 var segmentWasEdited = function segmentWasEdited(d) {
67265 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
67267 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
67271 return d.properties.nodes.some(function (n) {
67272 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
67277 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
67278 return 'way area target target-allowed ' + targetClass + d.id;
67279 }).classed('segment-edited', segmentWasEdited); // NOPE
67281 var nopeData = data.nopes.filter(getPath);
67282 var nopes = selection.selectAll('.area.target-nope').filter(function (d) {
67283 return filter(d.properties.entity);
67284 }).data(nopeData, function key(d) {
67288 nopes.exit().remove(); // enter/update
67290 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
67291 return 'way area target target-nope ' + nopeClass + d.id;
67292 }).classed('segment-edited', segmentWasEdited);
67295 function drawAreas(selection, graph, entities, filter) {
67296 var path = svgPath(projection, graph, true);
67299 var base = context.history().base();
67301 for (var i = 0; i < entities.length; i++) {
67302 var entity = entities[i];
67303 if (entity.geometry(graph) !== 'area') continue;
67304 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
67306 if (multipolygon) {
67307 areas[multipolygon.id] = {
67308 entity: multipolygon.mergeTags(entity.tags),
67309 area: Math.abs(entity.area(graph))
67311 } else if (!areas[entity.id]) {
67312 areas[entity.id] = {
67314 area: Math.abs(entity.area(graph))
67319 var fills = Object.values(areas).filter(function hasPath(a) {
67320 return path(a.entity);
67322 fills.sort(function areaSort(a, b) {
67323 return b.area - a.area;
67325 fills = fills.map(function (a) {
67328 var strokes = fills.filter(function (area) {
67329 return area.type === 'way';
67337 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm').filter(filter).data(data.clip, osmEntity.key);
67338 clipPaths.exit().remove();
67339 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-osm').attr('id', function (entity) {
67340 return 'ideditor-' + entity.id + '-clippath';
67342 clipPathsEnter.append('path');
67343 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', path);
67344 var drawLayer = selection.selectAll('.layer-osm.areas');
67345 var touchLayer = selection.selectAll('.layer-touch.areas'); // Draw areas..
67347 var areagroup = drawLayer.selectAll('g.areagroup').data(['fill', 'shadow', 'stroke']);
67348 areagroup = areagroup.enter().append('g').attr('class', function (d) {
67349 return 'areagroup area-' + d;
67350 }).merge(areagroup);
67351 var paths = areagroup.selectAll('path').filter(filter).data(function (layer) {
67352 return data[layer];
67354 paths.exit().remove();
67355 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
67356 var bisect = d3_bisector(function (node) {
67357 return -node.__data__.area(graph);
67360 function sortedByArea(entity) {
67361 if (this._parent.__data__ === 'fill') {
67362 return fillpaths[bisect(fillpaths, -entity.area(graph))];
67366 paths = paths.enter().insert('path', sortedByArea).merge(paths).each(function (entity) {
67367 var layer = this.parentNode.__data__;
67368 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
67370 if (layer === 'fill') {
67371 this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
67372 this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
67374 }).classed('added', function (d) {
67375 return !base.entities[d.id];
67376 }).classed('geometry-edited', function (d) {
67377 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
67378 }).classed('retagged', function (d) {
67379 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
67380 }).call(svgTagClasses()).attr('d', path); // Draw touch targets..
67382 touchLayer.call(drawTargets, graph, data.stroke, filter);
67388 //[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
67389 //[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
67390 //[5] Name ::= NameStartChar (NameChar)*
67391 var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/; //\u10000-\uEFFFF
67393 var nameChar = new RegExp("[\\-\\.0-9" + nameStartChar.source.slice(1, -1) + "\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
67394 var tagNamePattern = new RegExp('^' + nameStartChar.source + nameChar.source + '*(?:\:' + nameStartChar.source + nameChar.source + '*)?$'); //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
67395 //var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
67396 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
67397 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
67399 var S_TAG = 0; //tag name offerring
67401 var S_ATTR = 1; //attr name offerring
67403 var S_ATTR_SPACE = 2; //attr name end and space offer
67405 var S_EQ = 3; //=space?
67407 var S_ATTR_NOQUOT_VALUE = 4; //attr value(no quot value only)
67409 var S_ATTR_END = 5; //attr value end and no space(quot end)
67411 var S_TAG_SPACE = 6; //(attr value end || tag end ) && (space offer)
67413 var S_TAG_CLOSE = 7; //closed el<el />
67415 function XMLReader() {}
67417 XMLReader.prototype = {
67418 parse: function parse(source, defaultNSMap, entityMap) {
67419 var domBuilder = this.domBuilder;
67420 domBuilder.startDocument();
67422 _copy(defaultNSMap, defaultNSMap = {});
67424 _parse(source, defaultNSMap, entityMap, domBuilder, this.errorHandler);
67426 domBuilder.endDocument();
67430 function _parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
67431 function fixedFromCharCode(code) {
67432 // String.prototype.fromCharCode does not supports
67433 // > 2 bytes unicode chars directly
67434 if (code > 0xffff) {
67436 var surrogate1 = 0xd800 + (code >> 10),
67437 surrogate2 = 0xdc00 + (code & 0x3ff);
67438 return String.fromCharCode(surrogate1, surrogate2);
67440 return String.fromCharCode(code);
67444 function entityReplacer(a) {
67445 var k = a.slice(1, -1);
67447 if (k in entityMap) {
67448 return entityMap[k];
67449 } else if (k.charAt(0) === '#') {
67450 return fixedFromCharCode(parseInt(k.substr(1).replace('x', '0x')));
67452 errorHandler.error('entity not found:' + a);
67457 function appendText(end) {
67460 var xt = source.substring(start, end).replace(/&#?\w+;/g, entityReplacer);
67461 locator && position(start);
67462 domBuilder.characters(xt, 0, end - start);
67467 function position(p, m) {
67468 while (p >= lineEnd && (m = linePattern.exec(source))) {
67469 lineStart = m.index;
67470 lineEnd = lineStart + m[0].length;
67471 locator.lineNumber++; //console.log('line++:',locator,startPos,endPos)
67474 locator.columnNumber = p - lineStart + 1;
67479 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
67480 var locator = domBuilder.locator;
67481 var parseStack = [{
67482 currentNSMap: defaultNSMapCopy
67489 var tagStart = source.indexOf('<', start);
67491 if (tagStart < 0) {
67492 if (!source.substr(start).match(/^\s*$/)) {
67493 var doc = domBuilder.doc;
67494 var text = doc.createTextNode(source.substr(start));
67495 doc.appendChild(text);
67496 domBuilder.currentElement = text;
67502 if (tagStart > start) {
67503 appendText(tagStart);
67506 switch (source.charAt(tagStart + 1)) {
67508 var end = source.indexOf('>', tagStart + 3);
67509 var tagName = source.substring(tagStart + 2, end);
67510 var config = parseStack.pop();
67513 tagName = source.substring(tagStart + 2).replace(/[\s<].*/, ''); //console.error('#@@@@@@'+tagName)
67515 errorHandler.error("end tag name: " + tagName + ' is not complete:' + config.tagName);
67516 end = tagStart + 1 + tagName.length;
67517 } else if (tagName.match(/\s</)) {
67518 tagName = tagName.replace(/[\s<].*/, '');
67519 errorHandler.error("end tag name: " + tagName + ' maybe not complete');
67520 end = tagStart + 1 + tagName.length;
67521 } //console.error(parseStack.length,parseStack)
67522 //console.error(config);
67525 var localNSMap = config.localNSMap;
67526 var endMatch = config.tagName == tagName;
67527 var endIgnoreCaseMach = endMatch || config.tagName && config.tagName.toLowerCase() == tagName.toLowerCase();
67529 if (endIgnoreCaseMach) {
67530 domBuilder.endElement(config.uri, config.localName, tagName);
67533 for (var prefix in localNSMap) {
67534 domBuilder.endPrefixMapping(prefix);
67539 errorHandler.fatalError("end tag name: " + tagName + ' is not match the current start tagName:' + config.tagName);
67542 parseStack.push(config);
67551 locator && position(tagStart);
67552 end = parseInstruction(source, tagStart, domBuilder);
67556 // <!doctype,<![CDATA,<!--
67557 locator && position(tagStart);
67558 end = parseDCC(source, tagStart, domBuilder, errorHandler);
67562 locator && position(tagStart);
67563 var el = new ElementAttributes();
67564 var currentNSMap = parseStack[parseStack.length - 1].currentNSMap; //elStartEnd
67566 var end = parseElementStartPart(source, tagStart, el, currentNSMap, entityReplacer, errorHandler);
67567 var len = el.length;
67569 if (!el.closed && fixSelfClosed(source, end, el.tagName, closeMap)) {
67572 if (!entityMap.nbsp) {
67573 errorHandler.warning('unclosed xml attribute');
67577 if (locator && len) {
67578 var locator2 = copyLocator(locator, {}); //try{//attribute position fixed
67580 for (var i = 0; i < len; i++) {
67582 position(a.offset);
67583 a.locator = copyLocator(locator, {});
67584 } //}catch(e){console.error('@@@@@'+e)}
67587 domBuilder.locator = locator2;
67589 if (appendElement(el, domBuilder, currentNSMap)) {
67590 parseStack.push(el);
67593 domBuilder.locator = locator;
67595 if (appendElement(el, domBuilder, currentNSMap)) {
67596 parseStack.push(el);
67600 if (el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed) {
67601 end = parseHtmlSpecialContent(source, end, el.tagName, entityReplacer, domBuilder);
67608 errorHandler.error('element parse error: ' + e); //errorHandler.error('element parse error: '+e);
67610 end = -1; //throw e;
67616 //TODO: 这里有可能sax回退,有位置错误风险
67617 appendText(Math.max(tagStart, start) + 1);
67622 function copyLocator(f, t) {
67623 t.lineNumber = f.lineNumber;
67624 t.columnNumber = f.columnNumber;
67628 * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
67629 * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
67633 function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler) {
67637 var s = S_TAG; //status
67640 var c = source.charAt(p);
67644 if (s === S_ATTR) {
67646 attrName = source.slice(start, p);
67648 } else if (s === S_ATTR_SPACE) {
67651 //fatalError: equal must after attrName or space after attrName
67652 throw new Error('attribute equal must after attrName');
67659 if (s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
67662 if (s === S_ATTR) {
67663 errorHandler.warning('attribute value must after "="');
67664 attrName = source.slice(start, p);
67668 p = source.indexOf(c, start);
67671 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
67672 el.add(attrName, value, start - 1);
67675 //fatalError: no end quot match
67676 throw new Error('attribute value no end \'' + c + '\' match');
67678 } else if (s == S_ATTR_NOQUOT_VALUE) {
67679 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer); //console.log(attrName,value,start,p)
67681 el.add(attrName, value, start); //console.dir(el)
67683 errorHandler.warning('attribute "' + attrName + '" missed start quot(' + c + ')!!');
67687 //fatalError: no equal before
67688 throw new Error('attribute value must after "="');
67696 el.setTagName(source.slice(start, p));
67704 case S_ATTR_NOQUOT_VALUE:
67711 throw new Error("attribute invalid close char('/')");
67718 //throw new Error('unexpected end of input')
67719 errorHandler.error('unexpected end of input');
67722 el.setTagName(source.slice(start, p));
67730 el.setTagName(source.slice(start, p));
67738 case S_ATTR_NOQUOT_VALUE: //Compatible state
67741 value = source.slice(start, p);
67743 if (value.slice(-1) === '/') {
67745 value = value.slice(0, -1);
67749 if (s === S_ATTR_SPACE) {
67753 if (s == S_ATTR_NOQUOT_VALUE) {
67754 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
67755 el.add(attrName, value.replace(/&#?\w+;/g, entityReplacer), start);
67757 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)) {
67758 errorHandler.warning('attribute "' + value + '" missed value!! "' + value + '" instead!!');
67761 el.add(value, value, start);
67767 throw new Error('attribute value missed!!');
67768 } // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
67773 /*xml space '\x20' | #x9 | #xD | #xA; */
67783 el.setTagName(source.slice(start, p)); //tagName
67789 attrName = source.slice(start, p);
67793 case S_ATTR_NOQUOT_VALUE:
67794 var value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
67795 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
67796 el.add(attrName, value, start);
67801 //case S_TAG_SPACE:
67803 //case S_ATTR_SPACE:
67805 //case S_TAG_CLOSE:
67810 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
67811 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
67813 //case S_TAG:void();break;
67814 //case S_ATTR:void();break;
67815 //case S_ATTR_NOQUOT_VALUE:void();break;
67817 var tagName = el.tagName;
67819 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)) {
67820 errorHandler.warning('attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!');
67823 el.add(attrName, attrName, start);
67829 errorHandler.warning('attribute space is required"' + attrName + '"!!');
67837 s = S_ATTR_NOQUOT_VALUE;
67842 throw new Error("elements closed character '/' and '>' must be connected to");
67846 } //end outer switch
67847 //console.log('p++',p)
67854 * @return true if has new namespace define
67858 function appendElement(el, domBuilder, currentNSMap) {
67859 var tagName = el.tagName;
67860 var localNSMap = null; //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
67866 var qName = a.qName;
67867 var value = a.value;
67868 var nsp = qName.indexOf(':');
67871 var prefix = a.prefix = qName.slice(0, nsp);
67872 var localName = qName.slice(nsp + 1);
67873 var nsPrefix = prefix === 'xmlns' && localName;
67877 nsPrefix = qName === 'xmlns' && '';
67878 } //can not set prefix,because prefix !== ''
67881 a.localName = localName; //prefix == null for no ns prefix attribute
67883 if (nsPrefix !== false) {
67885 if (localNSMap == null) {
67886 localNSMap = {}; //console.log(currentNSMap,0)
67888 _copy(currentNSMap, currentNSMap = {}); //console.log(currentNSMap,1)
67892 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
67893 a.uri = 'http://www.w3.org/2000/xmlns/';
67894 domBuilder.startPrefixMapping(nsPrefix, value);
67902 var prefix = a.prefix;
67905 //no prefix attribute has no namespace
67906 if (prefix === 'xml') {
67907 a.uri = 'http://www.w3.org/XML/1998/namespace';
67910 if (prefix !== 'xmlns') {
67911 a.uri = currentNSMap[prefix || '']; //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
67916 var nsp = tagName.indexOf(':');
67919 prefix = el.prefix = tagName.slice(0, nsp);
67920 localName = el.localName = tagName.slice(nsp + 1);
67922 prefix = null; //important!!
67924 localName = el.localName = tagName;
67925 } //no prefix element has default namespace
67928 var ns = el.uri = currentNSMap[prefix || ''];
67929 domBuilder.startElement(ns, localName, tagName, el); //endPrefixMapping and startPrefixMapping have not any help for dom builder
67930 //localNSMap = null
67933 domBuilder.endElement(ns, localName, tagName);
67936 for (prefix in localNSMap) {
67937 domBuilder.endPrefixMapping(prefix);
67941 el.currentNSMap = currentNSMap;
67942 el.localNSMap = localNSMap; //parseStack.push(el);
67948 function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) {
67949 if (/^(?:script|textarea)$/i.test(tagName)) {
67950 var elEndStart = source.indexOf('</' + tagName + '>', elStartEnd);
67951 var text = source.substring(elStartEnd + 1, elEndStart);
67953 if (/[&<]/.test(text)) {
67954 if (/^script$/i.test(tagName)) {
67955 //if(!/\]\]>/.test(text)){
67956 //lexHandler.startCDATA();
67957 domBuilder.characters(text, 0, text.length); //lexHandler.endCDATA();
67959 return elEndStart; //}
67960 } //}else{//text area
67963 text = text.replace(/&#?\w+;/g, entityReplacer);
67964 domBuilder.characters(text, 0, text.length);
67965 return elEndStart; //}
67969 return elStartEnd + 1;
67972 function fixSelfClosed(source, elStartEnd, tagName, closeMap) {
67973 //if(tagName in closeMap){
67974 var pos = closeMap[tagName];
67977 //console.log(tagName)
67978 pos = source.lastIndexOf('</' + tagName + '>');
67980 if (pos < elStartEnd) {
67982 pos = source.lastIndexOf('</' + tagName);
67985 closeMap[tagName] = pos;
67988 return pos < elStartEnd; //}
67991 function _copy(source, target) {
67992 for (var n in source) {
67993 target[n] = source[n];
67997 function parseDCC(source, start, domBuilder, errorHandler) {
67998 //sure start with '<!'
67999 var next = source.charAt(start + 2);
68003 if (source.charAt(start + 3) === '-') {
68004 var end = source.indexOf('-->', start + 4); //append comment source.substring(4,end)//<!--
68007 domBuilder.comment(source, start + 4, end - start - 4);
68010 errorHandler.error("Unclosed comment");
68019 if (source.substr(start + 3, 6) == 'CDATA[') {
68020 var end = source.indexOf(']]>', start + 9);
68021 domBuilder.startCDATA();
68022 domBuilder.characters(source, start + 9, end - start - 9);
68023 domBuilder.endCDATA();
68026 //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
68029 var matchs = split$1(source, start);
68030 var len = matchs.length;
68032 if (len > 1 && /!doctype/i.test(matchs[0][0])) {
68033 var name = matchs[1][0];
68034 var pubid = len > 3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
68035 var sysid = len > 4 && matchs[4][0];
68036 var lastMatch = matchs[len - 1];
68037 domBuilder.startDTD(name, pubid && pubid.replace(/^(['"])(.*?)\1$/, '$2'), sysid && sysid.replace(/^(['"])(.*?)\1$/, '$2'));
68038 domBuilder.endDTD();
68039 return lastMatch.index + lastMatch[0].length;
68047 function parseInstruction(source, start, domBuilder) {
68048 var end = source.indexOf('?>', start);
68051 var match = source.substring(start, end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
68054 var len = match[0].length;
68055 domBuilder.processingInstruction(match[1], match[2]);
68070 function ElementAttributes(source) {}
68072 ElementAttributes.prototype = {
68073 setTagName: function setTagName(tagName) {
68074 if (!tagNamePattern.test(tagName)) {
68075 throw new Error('invalid tagName:' + tagName);
68078 this.tagName = tagName;
68080 add: function add(qName, value, offset) {
68081 if (!tagNamePattern.test(qName)) {
68082 throw new Error('invalid attribute:' + qName);
68085 this[this.length++] = {
68092 getLocalName: function getLocalName(i) {
68093 return this[i].localName;
68095 getLocator: function getLocator(i) {
68096 return this[i].locator;
68098 getQName: function getQName(i) {
68099 return this[i].qName;
68101 getURI: function getURI(i) {
68102 return this[i].uri;
68104 getValue: function getValue(i) {
68105 return this[i].value;
68106 } // ,getIndex:function(uri, localName)){
68113 // getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
68114 // getType:function(uri,localName){}
68115 // getType:function(i){},
68119 function _set_proto_(thiz, parent) {
68120 thiz.__proto__ = parent;
68124 if (!(_set_proto_({}, _set_proto_.prototype) instanceof _set_proto_)) {
68125 _set_proto_ = function _set_proto_(thiz, parent) {
68127 p.prototype = parent;
68130 for (parent in thiz) {
68131 p[parent] = thiz[parent];
68138 function split$1(source, start) {
68141 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
68142 reg.lastIndex = start;
68143 reg.exec(source); //skip <
68145 while (match = reg.exec(source)) {
68147 if (match[1]) return buf;
68151 var XMLReader_1 = XMLReader;
68153 XMLReader: XMLReader_1
68158 * Object DOMException
68159 * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
68160 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
68162 function copy$1(src, dest) {
68163 for (var p in src) {
68168 ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
68169 ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
68173 function _extends(Class, Super) {
68174 var pt = Class.prototype;
68176 if (Object.create) {
68177 var ppt = Object.create(Super.prototype);
68178 pt.__proto__ = ppt;
68181 if (!(pt instanceof Super)) {
68182 var t = function t() {};
68183 t.prototype = Super.prototype;
68186 Class.prototype = pt = t;
68189 if (pt.constructor != Class) {
68190 if (typeof Class != 'function') {
68191 console.error("unknow Class:" + Class);
68194 pt.constructor = Class;
68198 var htmlns = 'http://www.w3.org/1999/xhtml'; // Node Types
68201 var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
68202 var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
68203 var TEXT_NODE = NodeType.TEXT_NODE = 3;
68204 var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
68205 var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
68206 var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
68207 var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
68208 var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
68209 var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
68210 var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
68211 var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
68212 var NOTATION_NODE = NodeType.NOTATION_NODE = 12; // ExceptionCode
68214 var ExceptionCode = {};
68215 var ExceptionMessage = {};
68216 var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = (ExceptionMessage[1] = "Index size error", 1);
68217 var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = (ExceptionMessage[2] = "DOMString size error", 2);
68218 var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = (ExceptionMessage[3] = "Hierarchy request error", 3);
68219 var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = (ExceptionMessage[4] = "Wrong document", 4);
68220 var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = (ExceptionMessage[5] = "Invalid character", 5);
68221 var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = (ExceptionMessage[6] = "No data allowed", 6);
68222 var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = (ExceptionMessage[7] = "No modification allowed", 7);
68223 var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = (ExceptionMessage[8] = "Not found", 8);
68224 var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = (ExceptionMessage[9] = "Not supported", 9);
68225 var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = (ExceptionMessage[10] = "Attribute in use", 10); //level2
68227 var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = (ExceptionMessage[11] = "Invalid state", 11);
68228 var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = (ExceptionMessage[12] = "Syntax error", 12);
68229 var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = (ExceptionMessage[13] = "Invalid modification", 13);
68230 var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = (ExceptionMessage[14] = "Invalid namespace", 14);
68231 var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = (ExceptionMessage[15] = "Invalid access", 15);
68233 function DOMException$2(code, message) {
68234 if (message instanceof Error) {
68235 var error = message;
68238 Error.call(this, ExceptionMessage[code]);
68239 this.message = ExceptionMessage[code];
68240 if (Error.captureStackTrace) Error.captureStackTrace(this, DOMException$2);
68244 if (message) this.message = this.message + ": " + message;
68247 DOMException$2.prototype = Error.prototype;
68248 copy$1(ExceptionCode, DOMException$2);
68250 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
68251 * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
68252 * The items in the NodeList are accessible via an integral index, starting from 0.
68255 function NodeList() {}
68256 NodeList.prototype = {
68258 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
68264 * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
68266 * @param index unsigned long
68267 * Index into the collection.
68269 * The node at the indexth position in the NodeList, or null if that is not a valid index.
68271 item: function item(index) {
68272 return this[index] || null;
68274 toString: function toString(isHTML, nodeFilter) {
68275 for (var buf = [], i = 0; i < this.length; i++) {
68276 serializeToString(this[i], buf, isHTML, nodeFilter);
68279 return buf.join('');
68283 function LiveNodeList(node, refresh) {
68285 this._refresh = refresh;
68287 _updateLiveList(this);
68290 function _updateLiveList(list) {
68291 var inc = list._node._inc || list._node.ownerDocument._inc;
68293 if (list._inc != inc) {
68294 var ls = list._refresh(list._node); //console.log(ls.length)
68297 __set__(list, 'length', ls.length);
68304 LiveNodeList.prototype.item = function (i) {
68305 _updateLiveList(this);
68310 _extends(LiveNodeList, NodeList);
68313 * Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes.
68314 * NamedNodeMap objects in the DOM are live.
68315 * used for attributes or DocumentType entities
68319 function NamedNodeMap() {}
68321 function _findNodeIndex(list, node) {
68322 var i = list.length;
68325 if (list[i] === node) {
68331 function _addNamedNode(el, list, newAttr, oldAttr) {
68333 list[_findNodeIndex(list, oldAttr)] = newAttr;
68335 list[list.length++] = newAttr;
68339 newAttr.ownerElement = el;
68340 var doc = el.ownerDocument;
68343 oldAttr && _onRemoveAttribute(doc, el, oldAttr);
68345 _onAddAttribute(doc, el, newAttr);
68350 function _removeNamedNode(el, list, attr) {
68351 //console.log('remove attr:'+attr)
68352 var i = _findNodeIndex(list, attr);
68355 var lastIndex = list.length - 1;
68357 while (i < lastIndex) {
68358 list[i] = list[++i];
68361 list.length = lastIndex;
68364 var doc = el.ownerDocument;
68367 _onRemoveAttribute(doc, el, attr);
68369 attr.ownerElement = null;
68373 throw DOMException$2(NOT_FOUND_ERR, new Error(el.tagName + '@' + attr));
68377 NamedNodeMap.prototype = {
68379 item: NodeList.prototype.item,
68380 getNamedItem: function getNamedItem(key) {
68381 // if(key.indexOf(':')>0 || key == 'xmlns'){
68385 var i = this.length;
68388 var attr = this[i]; //console.log(attr.nodeName,key)
68390 if (attr.nodeName == key) {
68395 setNamedItem: function setNamedItem(attr) {
68396 var el = attr.ownerElement;
68398 if (el && el != this._ownerElement) {
68399 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
68402 var oldAttr = this.getNamedItem(attr.nodeName);
68404 _addNamedNode(this._ownerElement, this, attr, oldAttr);
68410 setNamedItemNS: function setNamedItemNS(attr) {
68411 // raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
68412 var el = attr.ownerElement,
68415 if (el && el != this._ownerElement) {
68416 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
68419 oldAttr = this.getNamedItemNS(attr.namespaceURI, attr.localName);
68421 _addNamedNode(this._ownerElement, this, attr, oldAttr);
68427 removeNamedItem: function removeNamedItem(key) {
68428 var attr = this.getNamedItem(key);
68430 _removeNamedNode(this._ownerElement, this, attr);
68434 // raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
68436 removeNamedItemNS: function removeNamedItemNS(namespaceURI, localName) {
68437 var attr = this.getNamedItemNS(namespaceURI, localName);
68439 _removeNamedNode(this._ownerElement, this, attr);
68443 getNamedItemNS: function getNamedItemNS(namespaceURI, localName) {
68444 var i = this.length;
68447 var node = this[i];
68449 if (node.localName == localName && node.namespaceURI == namespaceURI) {
68458 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
68461 function DOMImplementation(
68464 this._features = {};
68467 for (var feature in features) {
68468 this._features = features[feature];
68472 DOMImplementation.prototype = {
68473 hasFeature: function hasFeature(
68478 var versions = this._features[feature.toLowerCase()];
68480 if (versions && (!version || version in versions)) {
68486 // Introduced in DOM Level 2:
68487 createDocument: function createDocument(namespaceURI, qualifiedName, doctype) {
68488 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
68489 var doc = new Document();
68490 doc.implementation = this;
68491 doc.childNodes = new NodeList();
68492 doc.doctype = doctype;
68495 doc.appendChild(doctype);
68498 if (qualifiedName) {
68499 var root = doc.createElementNS(namespaceURI, qualifiedName);
68500 doc.appendChild(root);
68505 // Introduced in DOM Level 2:
68506 createDocumentType: function createDocumentType(qualifiedName, publicId, systemId) {
68507 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
68508 var node = new DocumentType();
68509 node.name = qualifiedName;
68510 node.nodeName = qualifiedName;
68511 node.publicId = publicId;
68512 node.systemId = systemId; // Introduced in DOM Level 2:
68513 //readonly attribute DOMString internalSubset;
68515 // readonly attribute NamedNodeMap entities;
68516 // readonly attribute NamedNodeMap notations;
68522 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
68529 previousSibling: null,
68534 ownerDocument: null,
68536 namespaceURI: null,
68539 // Modified in DOM Level 2:
68540 insertBefore: function insertBefore(newChild, refChild) {
68542 return _insertBefore(this, newChild, refChild);
68544 replaceChild: function replaceChild(newChild, oldChild) {
68546 this.insertBefore(newChild, oldChild);
68549 this.removeChild(oldChild);
68552 removeChild: function removeChild(oldChild) {
68553 return _removeChild(this, oldChild);
68555 appendChild: function appendChild(newChild) {
68556 return this.insertBefore(newChild, null);
68558 hasChildNodes: function hasChildNodes() {
68559 return this.firstChild != null;
68561 cloneNode: function cloneNode(deep) {
68562 return _cloneNode(this.ownerDocument || this, this, deep);
68564 // Modified in DOM Level 2:
68565 normalize: function normalize() {
68566 var child = this.firstChild;
68569 var next = child.nextSibling;
68571 if (next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE) {
68572 this.removeChild(next);
68573 child.appendData(next.data);
68580 // Introduced in DOM Level 2:
68581 isSupported: function isSupported(feature, version) {
68582 return this.ownerDocument.implementation.hasFeature(feature, version);
68584 // Introduced in DOM Level 2:
68585 hasAttributes: function hasAttributes() {
68586 return this.attributes.length > 0;
68588 lookupPrefix: function lookupPrefix(namespaceURI) {
68592 var map = el._nsMap; //console.dir(map)
68595 for (var n in map) {
68596 if (map[n] == namespaceURI) {
68602 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
68607 // Introduced in DOM Level 3:
68608 lookupNamespaceURI: function lookupNamespaceURI(prefix) {
68612 var map = el._nsMap; //console.dir(map)
68615 if (prefix in map) {
68616 return map[prefix];
68620 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
68625 // Introduced in DOM Level 3:
68626 isDefaultNamespace: function isDefaultNamespace(namespaceURI) {
68627 var prefix = this.lookupPrefix(namespaceURI);
68628 return prefix == null;
68632 function _xmlEncoder(c) {
68633 return c == '<' && '<' || c == '>' && '>' || c == '&' && '&' || c == '"' && '"' || '&#' + c.charCodeAt() + ';';
68636 copy$1(NodeType, Node);
68637 copy$1(NodeType, Node.prototype);
68639 * @param callback return true for continue,false for break
68640 * @return boolean true: break visit;
68643 function _visitNode(node, callback) {
68644 if (callback(node)) {
68648 if (node = node.firstChild) {
68650 if (_visitNode(node, callback)) {
68653 } while (node = node.nextSibling);
68657 function Document() {}
68659 function _onAddAttribute(doc, el, newAttr) {
68661 var ns = newAttr.namespaceURI;
68663 if (ns == 'http://www.w3.org/2000/xmlns/') {
68665 el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
68669 function _onRemoveAttribute(doc, el, newAttr, remove) {
68671 var ns = newAttr.namespaceURI;
68673 if (ns == 'http://www.w3.org/2000/xmlns/') {
68675 delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
68679 function _onUpdateChild(doc, el, newChild) {
68680 if (doc && doc._inc) {
68681 doc._inc++; //update childNodes
68683 var cs = el.childNodes;
68686 cs[cs.length++] = newChild;
68689 var child = el.firstChild;
68694 child = child.nextSibling;
68705 * writeable properties:
68706 * nodeValue,Attr:value,CharacterData:data
68711 function _removeChild(parentNode, child) {
68712 var previous = child.previousSibling;
68713 var next = child.nextSibling;
68716 previous.nextSibling = next;
68718 parentNode.firstChild = next;
68722 next.previousSibling = previous;
68724 parentNode.lastChild = previous;
68727 _onUpdateChild(parentNode.ownerDocument, parentNode);
68732 * preformance key(refChild == null)
68736 function _insertBefore(parentNode, newChild, nextChild) {
68737 var cp = newChild.parentNode;
68740 cp.removeChild(newChild); //remove and update
68743 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
68744 var newFirst = newChild.firstChild;
68746 if (newFirst == null) {
68750 var newLast = newChild.lastChild;
68752 newFirst = newLast = newChild;
68755 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
68756 newFirst.previousSibling = pre;
68757 newLast.nextSibling = nextChild;
68760 pre.nextSibling = newFirst;
68762 parentNode.firstChild = newFirst;
68765 if (nextChild == null) {
68766 parentNode.lastChild = newLast;
68768 nextChild.previousSibling = newLast;
68772 newFirst.parentNode = parentNode;
68773 } while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
68775 _onUpdateChild(parentNode.ownerDocument || parentNode, parentNode); //console.log(parentNode.lastChild.nextSibling == null)
68778 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
68779 newChild.firstChild = newChild.lastChild = null;
68785 function _appendSingleChild(parentNode, newChild) {
68786 var cp = newChild.parentNode;
68789 var pre = parentNode.lastChild;
68790 cp.removeChild(newChild); //remove and update
68792 var pre = parentNode.lastChild;
68795 var pre = parentNode.lastChild;
68796 newChild.parentNode = parentNode;
68797 newChild.previousSibling = pre;
68798 newChild.nextSibling = null;
68801 pre.nextSibling = newChild;
68803 parentNode.firstChild = newChild;
68806 parentNode.lastChild = newChild;
68808 _onUpdateChild(parentNode.ownerDocument, parentNode, newChild);
68810 return newChild; //console.log("__aa",parentNode.lastChild.nextSibling == null)
68813 Document.prototype = {
68814 //implementation : null,
68815 nodeName: '#document',
68816 nodeType: DOCUMENT_NODE,
68818 documentElement: null,
68820 insertBefore: function insertBefore(newChild, refChild) {
68822 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
68823 var child = newChild.firstChild;
68826 var next = child.nextSibling;
68827 this.insertBefore(child, refChild);
68834 if (this.documentElement == null && newChild.nodeType == ELEMENT_NODE) {
68835 this.documentElement = newChild;
68838 return _insertBefore(this, newChild, refChild), newChild.ownerDocument = this, newChild;
68840 removeChild: function removeChild(oldChild) {
68841 if (this.documentElement == oldChild) {
68842 this.documentElement = null;
68845 return _removeChild(this, oldChild);
68847 // Introduced in DOM Level 2:
68848 importNode: function importNode(importedNode, deep) {
68849 return _importNode(this, importedNode, deep);
68851 // Introduced in DOM Level 2:
68852 getElementById: function getElementById(id) {
68855 _visitNode(this.documentElement, function (node) {
68856 if (node.nodeType == ELEMENT_NODE) {
68857 if (node.getAttribute('id') == id) {
68866 //document factory method:
68867 createElement: function createElement(tagName) {
68868 var node = new Element();
68869 node.ownerDocument = this;
68870 node.nodeName = tagName;
68871 node.tagName = tagName;
68872 node.childNodes = new NodeList();
68873 var attrs = node.attributes = new NamedNodeMap();
68874 attrs._ownerElement = node;
68877 createDocumentFragment: function createDocumentFragment() {
68878 var node = new DocumentFragment();
68879 node.ownerDocument = this;
68880 node.childNodes = new NodeList();
68883 createTextNode: function createTextNode(data) {
68884 var node = new Text();
68885 node.ownerDocument = this;
68886 node.appendData(data);
68889 createComment: function createComment(data) {
68890 var node = new Comment();
68891 node.ownerDocument = this;
68892 node.appendData(data);
68895 createCDATASection: function createCDATASection(data) {
68896 var node = new CDATASection();
68897 node.ownerDocument = this;
68898 node.appendData(data);
68901 createProcessingInstruction: function createProcessingInstruction(target, data) {
68902 var node = new ProcessingInstruction();
68903 node.ownerDocument = this;
68904 node.tagName = node.target = target;
68905 node.nodeValue = node.data = data;
68908 createAttribute: function createAttribute(name) {
68909 var node = new Attr();
68910 node.ownerDocument = this;
68912 node.nodeName = name;
68913 node.localName = name;
68914 node.specified = true;
68917 createEntityReference: function createEntityReference(name) {
68918 var node = new EntityReference();
68919 node.ownerDocument = this;
68920 node.nodeName = name;
68923 // Introduced in DOM Level 2:
68924 createElementNS: function createElementNS(namespaceURI, qualifiedName) {
68925 var node = new Element();
68926 var pl = qualifiedName.split(':');
68927 var attrs = node.attributes = new NamedNodeMap();
68928 node.childNodes = new NodeList();
68929 node.ownerDocument = this;
68930 node.nodeName = qualifiedName;
68931 node.tagName = qualifiedName;
68932 node.namespaceURI = namespaceURI;
68934 if (pl.length == 2) {
68935 node.prefix = pl[0];
68936 node.localName = pl[1];
68938 //el.prefix = null;
68939 node.localName = qualifiedName;
68942 attrs._ownerElement = node;
68945 // Introduced in DOM Level 2:
68946 createAttributeNS: function createAttributeNS(namespaceURI, qualifiedName) {
68947 var node = new Attr();
68948 var pl = qualifiedName.split(':');
68949 node.ownerDocument = this;
68950 node.nodeName = qualifiedName;
68951 node.name = qualifiedName;
68952 node.namespaceURI = namespaceURI;
68953 node.specified = true;
68955 if (pl.length == 2) {
68956 node.prefix = pl[0];
68957 node.localName = pl[1];
68959 //el.prefix = null;
68960 node.localName = qualifiedName;
68967 _extends(Document, Node);
68969 function Element() {
68972 Element.prototype = {
68973 nodeType: ELEMENT_NODE,
68974 hasAttribute: function hasAttribute(name) {
68975 return this.getAttributeNode(name) != null;
68977 getAttribute: function getAttribute(name) {
68978 var attr = this.getAttributeNode(name);
68979 return attr && attr.value || '';
68981 getAttributeNode: function getAttributeNode(name) {
68982 return this.attributes.getNamedItem(name);
68984 setAttribute: function setAttribute(name, value) {
68985 var attr = this.ownerDocument.createAttribute(name);
68986 attr.value = attr.nodeValue = "" + value;
68987 this.setAttributeNode(attr);
68989 removeAttribute: function removeAttribute(name) {
68990 var attr = this.getAttributeNode(name);
68991 attr && this.removeAttributeNode(attr);
68993 //four real opeartion method
68994 appendChild: function appendChild(newChild) {
68995 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
68996 return this.insertBefore(newChild, null);
68998 return _appendSingleChild(this, newChild);
69001 setAttributeNode: function setAttributeNode(newAttr) {
69002 return this.attributes.setNamedItem(newAttr);
69004 setAttributeNodeNS: function setAttributeNodeNS(newAttr) {
69005 return this.attributes.setNamedItemNS(newAttr);
69007 removeAttributeNode: function removeAttributeNode(oldAttr) {
69008 //console.log(this == oldAttr.ownerElement)
69009 return this.attributes.removeNamedItem(oldAttr.nodeName);
69011 //get real attribute name,and remove it by removeAttributeNode
69012 removeAttributeNS: function removeAttributeNS(namespaceURI, localName) {
69013 var old = this.getAttributeNodeNS(namespaceURI, localName);
69014 old && this.removeAttributeNode(old);
69016 hasAttributeNS: function hasAttributeNS(namespaceURI, localName) {
69017 return this.getAttributeNodeNS(namespaceURI, localName) != null;
69019 getAttributeNS: function getAttributeNS(namespaceURI, localName) {
69020 var attr = this.getAttributeNodeNS(namespaceURI, localName);
69021 return attr && attr.value || '';
69023 setAttributeNS: function setAttributeNS(namespaceURI, qualifiedName, value) {
69024 var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
69025 attr.value = attr.nodeValue = "" + value;
69026 this.setAttributeNode(attr);
69028 getAttributeNodeNS: function getAttributeNodeNS(namespaceURI, localName) {
69029 return this.attributes.getNamedItemNS(namespaceURI, localName);
69031 getElementsByTagName: function getElementsByTagName(tagName) {
69032 return new LiveNodeList(this, function (base) {
69035 _visitNode(base, function (node) {
69036 if (node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)) {
69044 getElementsByTagNameNS: function getElementsByTagNameNS(namespaceURI, localName) {
69045 return new LiveNodeList(this, function (base) {
69048 _visitNode(base, function (node) {
69049 if (node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)) {
69058 Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
69059 Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
69061 _extends(Element, Node);
69064 Attr.prototype.nodeType = ATTRIBUTE_NODE;
69066 _extends(Attr, Node);
69068 function CharacterData() {}
69069 CharacterData.prototype = {
69071 substringData: function substringData(offset, count) {
69072 return this.data.substring(offset, offset + count);
69074 appendData: function appendData(text) {
69075 text = this.data + text;
69076 this.nodeValue = this.data = text;
69077 this.length = text.length;
69079 insertData: function insertData(offset, text) {
69080 this.replaceData(offset, 0, text);
69082 appendChild: function appendChild(newChild) {
69083 throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]);
69085 deleteData: function deleteData(offset, count) {
69086 this.replaceData(offset, count, "");
69088 replaceData: function replaceData(offset, count, text) {
69089 var start = this.data.substring(0, offset);
69090 var end = this.data.substring(offset + count);
69091 text = start + text + end;
69092 this.nodeValue = this.data = text;
69093 this.length = text.length;
69097 _extends(CharacterData, Node);
69102 nodeType: TEXT_NODE,
69103 splitText: function splitText(offset) {
69104 var text = this.data;
69105 var newText = text.substring(offset);
69106 text = text.substring(0, offset);
69107 this.data = this.nodeValue = text;
69108 this.length = text.length;
69109 var newNode = this.ownerDocument.createTextNode(newText);
69111 if (this.parentNode) {
69112 this.parentNode.insertBefore(newNode, this.nextSibling);
69119 _extends(Text, CharacterData);
69121 function Comment() {}
69122 Comment.prototype = {
69123 nodeName: "#comment",
69124 nodeType: COMMENT_NODE
69127 _extends(Comment, CharacterData);
69129 function CDATASection() {}
69130 CDATASection.prototype = {
69131 nodeName: "#cdata-section",
69132 nodeType: CDATA_SECTION_NODE
69135 _extends(CDATASection, CharacterData);
69137 function DocumentType() {}
69138 DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
69140 _extends(DocumentType, Node);
69142 function Notation() {}
69143 Notation.prototype.nodeType = NOTATION_NODE;
69145 _extends(Notation, Node);
69147 function Entity() {}
69148 Entity.prototype.nodeType = ENTITY_NODE;
69150 _extends(Entity, Node);
69152 function EntityReference() {}
69153 EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
69155 _extends(EntityReference, Node);
69157 function DocumentFragment() {}
69158 DocumentFragment.prototype.nodeName = "#document-fragment";
69159 DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
69161 _extends(DocumentFragment, Node);
69163 function ProcessingInstruction() {}
69165 ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
69167 _extends(ProcessingInstruction, Node);
69169 function XMLSerializer$1() {}
69171 XMLSerializer$1.prototype.serializeToString = function (node, isHtml, nodeFilter) {
69172 return nodeSerializeToString.call(node, isHtml, nodeFilter);
69175 Node.prototype.toString = nodeSerializeToString;
69177 function nodeSerializeToString(isHtml, nodeFilter) {
69179 var refNode = this.nodeType == 9 ? this.documentElement : this;
69180 var prefix = refNode.prefix;
69181 var uri = refNode.namespaceURI;
69183 if (uri && prefix == null) {
69184 //console.log(prefix)
69185 var prefix = refNode.lookupPrefix(uri);
69187 if (prefix == null) {
69189 var visibleNamespaces = [{
69192 } //{namespace:uri,prefix:''}
69197 serializeToString(this, buf, isHtml, nodeFilter, visibleNamespaces); //console.log('###',this.nodeType,uri,prefix,buf.join(''))
69199 return buf.join('');
69202 function needNamespaceDefine(node, isHTML, visibleNamespaces) {
69203 var prefix = node.prefix || '';
69204 var uri = node.namespaceURI;
69206 if (!prefix && !uri) {
69210 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" || uri == 'http://www.w3.org/2000/xmlns/') {
69214 var i = visibleNamespaces.length; //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
69217 var ns = visibleNamespaces[i]; // get namespace prefix
69218 //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
69220 if (ns.prefix == prefix) {
69221 return ns.namespace != uri;
69223 } //console.log(isHTML,uri,prefix=='')
69224 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
69227 //node.flag = '11111'
69228 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
69234 function serializeToString(node, buf, isHTML, nodeFilter, visibleNamespaces) {
69236 node = nodeFilter(node);
69239 if (typeof node == 'string') {
69245 } //buf.sort.apply(attrs, attributeSorter);
69249 switch (node.nodeType) {
69251 if (!visibleNamespaces) visibleNamespaces = [];
69252 var startVisibleNamespaces = visibleNamespaces.length;
69253 var attrs = node.attributes;
69254 var len = attrs.length;
69255 var child = node.firstChild;
69256 var nodeName = node.tagName;
69257 isHTML = htmlns === node.namespaceURI || isHTML;
69258 buf.push('<', nodeName);
69260 for (var i = 0; i < len; i++) {
69261 // add namespaces for attributes
69262 var attr = attrs.item(i);
69264 if (attr.prefix == 'xmlns') {
69265 visibleNamespaces.push({
69266 prefix: attr.localName,
69267 namespace: attr.value
69269 } else if (attr.nodeName == 'xmlns') {
69270 visibleNamespaces.push({
69272 namespace: attr.value
69277 for (var i = 0; i < len; i++) {
69278 var attr = attrs.item(i);
69280 if (needNamespaceDefine(attr, isHTML, visibleNamespaces)) {
69281 var prefix = attr.prefix || '';
69282 var uri = attr.namespaceURI;
69283 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
69284 buf.push(ns, '="', uri, '"');
69285 visibleNamespaces.push({
69291 serializeToString(attr, buf, isHTML, nodeFilter, visibleNamespaces);
69292 } // add namespace for current node
69295 if (needNamespaceDefine(node, isHTML, visibleNamespaces)) {
69296 var prefix = node.prefix || '';
69297 var uri = node.namespaceURI;
69298 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
69299 buf.push(ns, '="', uri, '"');
69300 visibleNamespaces.push({
69306 if (child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)) {
69307 buf.push('>'); //if is cdata child node
69309 if (isHTML && /^script$/i.test(nodeName)) {
69312 buf.push(child.data);
69314 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69317 child = child.nextSibling;
69321 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69322 child = child.nextSibling;
69326 buf.push('</', nodeName, '>');
69329 } // remove added visible namespaces
69330 //visibleNamespaces.length = startVisibleNamespaces;
69335 case DOCUMENT_NODE:
69336 case DOCUMENT_FRAGMENT_NODE:
69337 var child = node.firstChild;
69340 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69341 child = child.nextSibling;
69346 case ATTRIBUTE_NODE:
69347 return buf.push(' ', node.name, '="', node.value.replace(/[<&"]/g, _xmlEncoder), '"');
69350 return buf.push(node.data.replace(/[<&]/g, _xmlEncoder));
69352 case CDATA_SECTION_NODE:
69353 return buf.push('<![CDATA[', node.data, ']]>');
69356 return buf.push("<!--", node.data, "-->");
69358 case DOCUMENT_TYPE_NODE:
69359 var pubid = node.publicId;
69360 var sysid = node.systemId;
69361 buf.push('<!DOCTYPE ', node.name);
69364 buf.push(' PUBLIC "', pubid);
69366 if (sysid && sysid != '.') {
69367 buf.push('" "', sysid);
69371 } else if (sysid && sysid != '.') {
69372 buf.push(' SYSTEM "', sysid, '">');
69374 var sub = node.internalSubset;
69377 buf.push(" [", sub, "]");
69385 case PROCESSING_INSTRUCTION_NODE:
69386 return buf.push("<?", node.target, " ", node.data, "?>");
69388 case ENTITY_REFERENCE_NODE:
69389 return buf.push('&', node.nodeName, ';');
69390 //case ENTITY_NODE:
69391 //case NOTATION_NODE:
69394 buf.push('??', node.nodeName);
69398 function _importNode(doc, node, deep) {
69401 switch (node.nodeType) {
69403 node2 = node.cloneNode(false);
69404 node2.ownerDocument = doc;
69405 //var attrs = node2.attributes;
69406 //var len = attrs.length;
69407 //for(var i=0;i<len;i++){
69408 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
69411 case DOCUMENT_FRAGMENT_NODE:
69414 case ATTRIBUTE_NODE:
69417 //case ENTITY_REFERENCE_NODE:
69418 //case PROCESSING_INSTRUCTION_NODE:
69419 ////case TEXT_NODE:
69420 //case CDATA_SECTION_NODE:
69421 //case COMMENT_NODE:
69424 //case DOCUMENT_NODE:
69425 //case DOCUMENT_TYPE_NODE:
69426 //cannot be imported.
69427 //case ENTITY_NODE:
69428 //case NOTATION_NODE:
69429 //can not hit in level3
69434 node2 = node.cloneNode(false); //false
69437 node2.ownerDocument = doc;
69438 node2.parentNode = null;
69441 var child = node.firstChild;
69444 node2.appendChild(_importNode(doc, child, deep));
69445 child = child.nextSibling;
69451 //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
69452 // attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
69455 function _cloneNode(doc, node, deep) {
69456 var node2 = new node.constructor();
69458 for (var n in node) {
69461 if (_typeof(v) != 'object') {
69462 if (v != node2[n]) {
69468 if (node.childNodes) {
69469 node2.childNodes = new NodeList();
69472 node2.ownerDocument = doc;
69474 switch (node2.nodeType) {
69476 var attrs = node.attributes;
69477 var attrs2 = node2.attributes = new NamedNodeMap();
69478 var len = attrs.length;
69479 attrs2._ownerElement = node2;
69481 for (var i = 0; i < len; i++) {
69482 node2.setAttributeNode(_cloneNode(doc, attrs.item(i), true));
69487 case ATTRIBUTE_NODE:
69492 var child = node.firstChild;
69495 node2.appendChild(_cloneNode(doc, child, deep));
69496 child = child.nextSibling;
69503 function __set__(object, key, value) {
69504 object[key] = value;
69509 if (Object.defineProperty) {
69510 var getTextContent = function getTextContent(node) {
69511 switch (node.nodeType) {
69513 case DOCUMENT_FRAGMENT_NODE:
69515 node = node.firstChild;
69518 if (node.nodeType !== 7 && node.nodeType !== 8) {
69519 buf.push(getTextContent(node));
69522 node = node.nextSibling;
69525 return buf.join('');
69528 return node.nodeValue;
69532 Object.defineProperty(LiveNodeList.prototype, 'length', {
69533 get: function get() {
69534 _updateLiveList(this);
69536 return this.$$length;
69539 Object.defineProperty(Node.prototype, 'textContent', {
69540 get: function get() {
69541 return getTextContent(this);
69543 set: function set(data) {
69544 switch (this.nodeType) {
69546 case DOCUMENT_FRAGMENT_NODE:
69547 while (this.firstChild) {
69548 this.removeChild(this.firstChild);
69551 if (data || String(data)) {
69552 this.appendChild(this.ownerDocument.createTextNode(data));
69561 this.nodeValue = data;
69566 __set__ = function __set__(object, key, value) {
69567 //console.log(value)
69568 object['$$' + key] = value;
69572 } //if(typeof require == 'function'){
69575 var DOMImplementation_1 = DOMImplementation;
69576 var XMLSerializer_1 = XMLSerializer$1; //}
69579 DOMImplementation: DOMImplementation_1,
69580 XMLSerializer: XMLSerializer_1
69583 var domParser = createCommonjsModule(function (module, exports) {
69584 function DOMParser(options) {
69585 this.options = options || {
69590 DOMParser.prototype.parseFromString = function (source, mimeType) {
69591 var options = this.options;
69592 var sax = new XMLReader();
69593 var domBuilder = options.domBuilder || new DOMHandler(); //contentHandler and LexicalHandler
69595 var errorHandler = options.errorHandler;
69596 var locator = options.locator;
69597 var defaultNSMap = options.xmlns || {};
69607 domBuilder.setDocumentLocator(locator);
69610 sax.errorHandler = buildErrorHandler(errorHandler, domBuilder, locator);
69611 sax.domBuilder = options.domBuilder || domBuilder;
69613 if (/\/x?html?$/.test(mimeType)) {
69614 entityMap.nbsp = '\xa0';
69615 entityMap.copy = '\xa9';
69616 defaultNSMap[''] = 'http://www.w3.org/1999/xhtml';
69619 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
69622 sax.parse(source, defaultNSMap, entityMap);
69624 sax.errorHandler.error("invalid doc source");
69627 return domBuilder.doc;
69630 function buildErrorHandler(errorImpl, domBuilder, locator) {
69632 if (domBuilder instanceof DOMHandler) {
69636 errorImpl = domBuilder;
69639 var errorHandler = {};
69640 var isCallback = errorImpl instanceof Function;
69641 locator = locator || {};
69643 function build(key) {
69644 var fn = errorImpl[key];
69646 if (!fn && isCallback) {
69647 fn = errorImpl.length == 2 ? function (msg) {
69648 errorImpl(key, msg);
69652 errorHandler[key] = fn && function (msg) {
69653 fn('[xmldom ' + key + ']\t' + msg + _locator(locator));
69654 } || function () {};
69659 build('fatalError');
69660 return errorHandler;
69661 } //console.log('#\n\n\n\n\n\n\n####')
69664 * +ContentHandler+ErrorHandler
69665 * +LexicalHandler+EntityResolver2
69666 * -DeclHandler-DTDHandler
69668 * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
69669 * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
69670 * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
69674 function DOMHandler() {
69675 this.cdata = false;
69678 function position(locator, node) {
69679 node.lineNumber = locator.lineNumber;
69680 node.columnNumber = locator.columnNumber;
69683 * @see org.xml.sax.ContentHandler#startDocument
69684 * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
69688 DOMHandler.prototype = {
69689 startDocument: function startDocument() {
69690 this.doc = new DOMImplementation().createDocument(null, null, null);
69692 if (this.locator) {
69693 this.doc.documentURI = this.locator.systemId;
69696 startElement: function startElement(namespaceURI, localName, qName, attrs) {
69697 var doc = this.doc;
69698 var el = doc.createElementNS(namespaceURI, qName || localName);
69699 var len = attrs.length;
69700 appendElement(this, el);
69701 this.currentElement = el;
69702 this.locator && position(this.locator, el);
69704 for (var i = 0; i < len; i++) {
69705 var namespaceURI = attrs.getURI(i);
69706 var value = attrs.getValue(i);
69707 var qName = attrs.getQName(i);
69708 var attr = doc.createAttributeNS(namespaceURI, qName);
69709 this.locator && position(attrs.getLocator(i), attr);
69710 attr.value = attr.nodeValue = value;
69711 el.setAttributeNode(attr);
69714 endElement: function endElement(namespaceURI, localName, qName) {
69715 var current = this.currentElement;
69716 var tagName = current.tagName;
69717 this.currentElement = current.parentNode;
69719 startPrefixMapping: function startPrefixMapping(prefix, uri) {},
69720 endPrefixMapping: function endPrefixMapping(prefix) {},
69721 processingInstruction: function processingInstruction(target, data) {
69722 var ins = this.doc.createProcessingInstruction(target, data);
69723 this.locator && position(this.locator, ins);
69724 appendElement(this, ins);
69726 ignorableWhitespace: function ignorableWhitespace(ch, start, length) {},
69727 characters: function characters(chars, start, length) {
69728 chars = _toString.apply(this, arguments); //console.log(chars)
69732 var charNode = this.doc.createCDATASection(chars);
69734 var charNode = this.doc.createTextNode(chars);
69737 if (this.currentElement) {
69738 this.currentElement.appendChild(charNode);
69739 } else if (/^\s*$/.test(chars)) {
69740 this.doc.appendChild(charNode); //process xml
69743 this.locator && position(this.locator, charNode);
69746 skippedEntity: function skippedEntity(name) {},
69747 endDocument: function endDocument() {
69748 this.doc.normalize();
69750 setDocumentLocator: function setDocumentLocator(locator) {
69751 if (this.locator = locator) {
69752 // && !('lineNumber' in locator)){
69753 locator.lineNumber = 0;
69757 comment: function comment(chars, start, length) {
69758 chars = _toString.apply(this, arguments);
69759 var comm = this.doc.createComment(chars);
69760 this.locator && position(this.locator, comm);
69761 appendElement(this, comm);
69763 startCDATA: function startCDATA() {
69764 //used in characters() methods
69767 endCDATA: function endCDATA() {
69768 this.cdata = false;
69770 startDTD: function startDTD(name, publicId, systemId) {
69771 var impl = this.doc.implementation;
69773 if (impl && impl.createDocumentType) {
69774 var dt = impl.createDocumentType(name, publicId, systemId);
69775 this.locator && position(this.locator, dt);
69776 appendElement(this, dt);
69781 * @see org.xml.sax.ErrorHandler
69782 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
69784 warning: function warning(error) {
69785 console.warn('[xmldom warning]\t' + error, _locator(this.locator));
69787 error: function error(_error) {
69788 console.error('[xmldom error]\t' + _error, _locator(this.locator));
69790 fatalError: function fatalError(error) {
69791 console.error('[xmldom fatalError]\t' + error, _locator(this.locator));
69796 function _locator(l) {
69798 return '\n@' + (l.systemId || '') + '#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']';
69802 function _toString(chars, start, length) {
69803 if (typeof chars == 'string') {
69804 return chars.substr(start, length);
69806 //java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
69807 if (chars.length >= start + length || start) {
69808 return new java.lang.String(chars, start, length) + '';
69815 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
69816 * used method of org.xml.sax.ext.LexicalHandler:
69817 * #comment(chars, start, length)
69820 * #startDTD(name, publicId, systemId)
69823 * IGNORED method of org.xml.sax.ext.LexicalHandler:
69825 * #startEntity(name)
69829 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
69830 * IGNORED method of org.xml.sax.ext.DeclHandler
69831 * #attributeDecl(eName, aName, type, mode, value)
69832 * #elementDecl(name, model)
69833 * #externalEntityDecl(name, publicId, systemId)
69834 * #internalEntityDecl(name, value)
69835 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
69836 * IGNORED method of org.xml.sax.EntityResolver2
69837 * #resolveEntity(String name,String publicId,String baseURI,String systemId)
69838 * #resolveEntity(publicId, systemId)
69839 * #getExternalSubset(name, baseURI)
69840 * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
69841 * IGNORED method of org.xml.sax.DTDHandler
69842 * #notationDecl(name, publicId, systemId) {};
69843 * #unparsedEntityDecl(name, publicId, systemId, notationName) {};
69847 "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g, function (key) {
69848 DOMHandler.prototype[key] = function () {
69852 /* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
69854 function appendElement(hander, node) {
69855 if (!hander.currentElement) {
69856 hander.doc.appendChild(node);
69858 hander.currentElement.appendChild(node);
69860 } //appendChild and setAttributeNS are preformance key
69861 //if(typeof require == 'function'){
69864 var XMLReader = sax.XMLReader;
69865 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
69866 exports.XMLSerializer = dom.XMLSerializer;
69867 exports.DOMParser = DOMParser; //}
69870 var togeojson = createCommonjsModule(function (module, exports) {
69871 var toGeoJSON = function () {
69873 var removeSpace = /\s*/g,
69874 trimSpace = /^\s*|\s*$/g,
69875 splitSpace = /\s+/; // generate a short, numeric hash of a string
69877 function okhash(x) {
69878 if (!x || !x.length) return 0;
69880 for (var i = 0, h = 0; i < x.length; i++) {
69881 h = (h << 5) - h + x.charCodeAt(i) | 0;
69885 } // all Y children of X
69888 function get(x, y) {
69889 return x.getElementsByTagName(y);
69892 function attr(x, y) {
69893 return x.getAttribute(y);
69896 function attrf(x, y) {
69897 return parseFloat(attr(x, y));
69898 } // one Y child of X, if any, otherwise null
69901 function get1(x, y) {
69903 return n.length ? n[0] : null;
69904 } // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
69907 function norm(el) {
69908 if (el.normalize) {
69913 } // cast array x into numbers
69916 function numarray(x) {
69917 for (var j = 0, o = []; j < x.length; j++) {
69918 o[j] = parseFloat(x[j]);
69922 } // get the content of a text node, if any
69925 function nodeVal(x) {
69930 return x && x.textContent || '';
69931 } // get the contents of multiple text nodes, if present
69934 function getMulti(x, ys) {
69939 for (k = 0; k < ys.length; k++) {
69940 n = get1(x, ys[k]);
69941 if (n) o[ys[k]] = nodeVal(n);
69945 } // add properties of Y to X, overwriting if present in both
69948 function extend(x, y) {
69952 } // get one coordinate from a coordinate array, if any
69955 function coord1(v) {
69956 return numarray(v.replace(removeSpace, '').split(','));
69957 } // get all coordinates from a coordinate array as [[],[]]
69960 function coord(v) {
69961 var coords = v.replace(trimSpace, '').split(splitSpace),
69964 for (var i = 0; i < coords.length; i++) {
69965 o.push(coord1(coords[i]));
69971 function coordPair(x) {
69972 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
69973 ele = get1(x, 'ele'),
69974 // handle namespaced attribute in browser
69975 heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
69976 time = get1(x, 'time'),
69980 e = parseFloat(nodeVal(ele));
69989 time: time ? nodeVal(time) : null,
69990 heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
69992 } // create a new feature collection parent object
69997 type: 'FeatureCollection',
70004 if (typeof XMLSerializer !== 'undefined') {
70005 /* istanbul ignore next */
70006 serializer = new XMLSerializer(); // only require xmldom in a node environment
70007 } else if ( (typeof process === "undefined" ? "undefined" : _typeof(process)) === 'object' && !process.browser) {
70008 serializer = new domParser.XMLSerializer();
70011 function xml2str(str) {
70012 // IE9 will create a new XMLSerializer but it'll crash immediately.
70013 // This line is ignored because we don't run coverage tests in IE9
70015 /* istanbul ignore next */
70016 if (str.xml !== undefined) return str.xml;
70017 return serializer.serializeToString(str);
70021 kml: function kml(doc) {
70023 // styleindex keeps track of hashed styles in order to match features
70026 // stylemapindex keeps track of style maps to expose in properties
70027 styleMapIndex = {},
70028 // atomic geospatial types supported by KML - MultiGeometry is
70029 // handled separately
70030 geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
70031 // all root placemarks in the file
70032 placemarks = get(doc, 'Placemark'),
70033 styles = get(doc, 'Style'),
70034 styleMaps = get(doc, 'StyleMap');
70036 for (var k = 0; k < styles.length; k++) {
70037 var hash = okhash(xml2str(styles[k])).toString(16);
70038 styleIndex['#' + attr(styles[k], 'id')] = hash;
70039 styleByHash[hash] = styles[k];
70042 for (var l = 0; l < styleMaps.length; l++) {
70043 styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
70044 var pairs = get(styleMaps[l], 'Pair');
70047 for (var m = 0; m < pairs.length; m++) {
70048 pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
70051 styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
70054 for (var j = 0; j < placemarks.length; j++) {
70055 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
70058 function kmlColor(v) {
70059 var color, opacity;
70062 if (v.substr(0, 1) === '#') {
70066 if (v.length === 6 || v.length === 3) {
70070 if (v.length === 8) {
70071 opacity = parseInt(v.substr(0, 2), 16) / 255;
70072 color = '#' + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2);
70075 return [color, isNaN(opacity) ? undefined : opacity];
70078 function gxCoord(v) {
70079 return numarray(v.split(' '));
70082 function gxCoords(root) {
70083 var elems = get(root, 'coord'),
70086 if (elems.length === 0) elems = get(root, 'gx:coord');
70088 for (var i = 0; i < elems.length; i++) {
70089 coords.push(gxCoord(nodeVal(elems[i])));
70092 var timeElems = get(root, 'when');
70094 for (var j = 0; j < timeElems.length; j++) {
70095 times.push(nodeVal(timeElems[j]));
70104 function getGeometry(root) {
70113 if (get1(root, 'MultiGeometry')) {
70114 return getGeometry(get1(root, 'MultiGeometry'));
70117 if (get1(root, 'MultiTrack')) {
70118 return getGeometry(get1(root, 'MultiTrack'));
70121 if (get1(root, 'gx:MultiTrack')) {
70122 return getGeometry(get1(root, 'gx:MultiTrack'));
70125 for (i = 0; i < geotypes.length; i++) {
70126 geomNodes = get(root, geotypes[i]);
70129 for (j = 0; j < geomNodes.length; j++) {
70130 geomNode = geomNodes[j];
70132 if (geotypes[i] === 'Point') {
70135 coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
70137 } else if (geotypes[i] === 'LineString') {
70139 type: 'LineString',
70140 coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
70142 } else if (geotypes[i] === 'Polygon') {
70143 var rings = get(geomNode, 'LinearRing'),
70146 for (k = 0; k < rings.length; k++) {
70147 coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
70152 coordinates: coords
70154 } else if (geotypes[i] === 'Track' || geotypes[i] === 'gx:Track') {
70155 var track = gxCoords(geomNode);
70157 type: 'LineString',
70158 coordinates: track.coords
70160 if (track.times.length) coordTimes.push(track.times);
70168 coordTimes: coordTimes
70172 function getPlacemark(root) {
70173 var geomsAndTimes = getGeometry(root),
70176 name = nodeVal(get1(root, 'name')),
70177 address = nodeVal(get1(root, 'address')),
70178 styleUrl = nodeVal(get1(root, 'styleUrl')),
70179 description = nodeVal(get1(root, 'description')),
70180 timeSpan = get1(root, 'TimeSpan'),
70181 timeStamp = get1(root, 'TimeStamp'),
70182 extendedData = get1(root, 'ExtendedData'),
70183 lineStyle = get1(root, 'LineStyle'),
70184 polyStyle = get1(root, 'PolyStyle'),
70185 visibility = get1(root, 'visibility');
70186 if (!geomsAndTimes.geoms.length) return [];
70187 if (name) properties.name = name;
70188 if (address) properties.address = address;
70191 if (styleUrl[0] !== '#') {
70192 styleUrl = '#' + styleUrl;
70195 properties.styleUrl = styleUrl;
70197 if (styleIndex[styleUrl]) {
70198 properties.styleHash = styleIndex[styleUrl];
70201 if (styleMapIndex[styleUrl]) {
70202 properties.styleMapHash = styleMapIndex[styleUrl];
70203 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
70204 } // Try to populate the lineStyle or polyStyle since we got the style hash
70207 var style = styleByHash[properties.styleHash];
70210 if (!lineStyle) lineStyle = get1(style, 'LineStyle');
70211 if (!polyStyle) polyStyle = get1(style, 'PolyStyle');
70215 if (description) properties.description = description;
70218 var begin = nodeVal(get1(timeSpan, 'begin'));
70219 var end = nodeVal(get1(timeSpan, 'end'));
70220 properties.timespan = {
70227 properties.timestamp = nodeVal(get1(timeStamp, 'when'));
70231 var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
70232 color = linestyles[0],
70233 opacity = linestyles[1],
70234 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
70235 if (color) properties.stroke = color;
70236 if (!isNaN(opacity)) properties['stroke-opacity'] = opacity;
70237 if (!isNaN(width)) properties['stroke-width'] = width;
70241 var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
70242 pcolor = polystyles[0],
70243 popacity = polystyles[1],
70244 fill = nodeVal(get1(polyStyle, 'fill')),
70245 outline = nodeVal(get1(polyStyle, 'outline'));
70246 if (pcolor) properties.fill = pcolor;
70247 if (!isNaN(popacity)) properties['fill-opacity'] = popacity;
70248 if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0;
70249 if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0;
70252 if (extendedData) {
70253 var datas = get(extendedData, 'Data'),
70254 simpleDatas = get(extendedData, 'SimpleData');
70256 for (i = 0; i < datas.length; i++) {
70257 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
70260 for (i = 0; i < simpleDatas.length; i++) {
70261 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
70266 properties.visibility = nodeVal(visibility);
70269 if (geomsAndTimes.coordTimes.length) {
70270 properties.coordTimes = geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
70275 geometry: geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : {
70276 type: 'GeometryCollection',
70277 geometries: geomsAndTimes.geoms
70279 properties: properties
70281 if (attr(root, 'id')) feature.id = attr(root, 'id');
70287 gpx: function gpx(doc) {
70289 tracks = get(doc, 'trk'),
70290 routes = get(doc, 'rte'),
70291 waypoints = get(doc, 'wpt'),
70292 // a feature collection
70296 for (i = 0; i < tracks.length; i++) {
70297 feature = getTrack(tracks[i]);
70298 if (feature) gj.features.push(feature);
70301 for (i = 0; i < routes.length; i++) {
70302 feature = getRoute(routes[i]);
70303 if (feature) gj.features.push(feature);
70306 for (i = 0; i < waypoints.length; i++) {
70307 gj.features.push(getPoint(waypoints[i]));
70310 function getPoints(node, pointname) {
70311 var pts = get(node, pointname),
70316 if (l < 2) return {}; // Invalid line in GeoJSON
70318 for (var i = 0; i < l; i++) {
70319 var c = coordPair(pts[i]);
70320 line.push(c.coordinates);
70321 if (c.time) times.push(c.time);
70322 if (c.heartRate) heartRates.push(c.heartRate);
70328 heartRates: heartRates
70332 function getTrack(node) {
70333 var segments = get(node, 'trkseg'),
70339 for (var i = 0; i < segments.length; i++) {
70340 line = getPoints(segments[i], 'trkpt');
70343 if (line.line) track.push(line.line);
70344 if (line.times && line.times.length) times.push(line.times);
70345 if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates);
70349 if (track.length === 0) return;
70350 var properties = getProperties(node);
70351 extend(properties, getLineStyle(get1(node, 'extensions')));
70352 if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times;
70353 if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates;
70356 properties: properties,
70358 type: track.length === 1 ? 'LineString' : 'MultiLineString',
70359 coordinates: track.length === 1 ? track[0] : track
70364 function getRoute(node) {
70365 var line = getPoints(node, 'rtept');
70366 if (!line.line) return;
70367 var prop = getProperties(node);
70368 extend(prop, getLineStyle(get1(node, 'extensions')));
70373 type: 'LineString',
70374 coordinates: line.line
70380 function getPoint(node) {
70381 var prop = getProperties(node);
70382 extend(prop, getMulti(node, ['sym']));
70388 coordinates: coordPair(node).coordinates
70393 function getLineStyle(extensions) {
70397 var lineStyle = get1(extensions, 'line');
70400 var color = nodeVal(get1(lineStyle, 'color')),
70401 opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
70402 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
70403 if (color) style.stroke = color;
70404 if (!isNaN(opacity)) style['stroke-opacity'] = opacity; // GPX width is in mm, convert to px with 96 px per inch
70406 if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4;
70413 function getProperties(node) {
70414 var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
70415 links = get(node, 'link');
70416 if (links.length) prop.links = [];
70418 for (var i = 0, link; i < links.length; i++) {
70420 href: attr(links[i], 'href')
70422 extend(link, getMulti(links[i], ['text', 'type']));
70423 prop.links.push(link);
70435 module.exports = toGeoJSON;
70438 var _initialized = false;
70439 var _enabled = false;
70443 function svgData(projection, context, dispatch) {
70444 var throttledRedraw = throttle(function () {
70445 dispatch.call('change');
70448 var _showLabels = true;
70449 var detected = utilDetect();
70450 var layer = select(null);
70461 if (_initialized) return; // run once
70466 function over(d3_event) {
70467 d3_event.stopPropagation();
70468 d3_event.preventDefault();
70469 d3_event.dataTransfer.dropEffect = 'copy';
70472 context.container().attr('dropzone', 'copy').on('drop.svgData', function (d3_event) {
70473 d3_event.stopPropagation();
70474 d3_event.preventDefault();
70475 if (!detected.filedrop) return;
70476 drawData.fileList(d3_event.dataTransfer.files);
70477 }).on('dragenter.svgData', over).on('dragexit.svgData', over).on('dragover.svgData', over);
70478 _initialized = true;
70481 function getService() {
70482 if (services.vectorTile && !_vtService) {
70483 _vtService = services.vectorTile;
70485 _vtService.event.on('loadedData', throttledRedraw);
70486 } else if (!services.vectorTile && _vtService) {
70493 function showLayer() {
70495 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
70496 dispatch.call('change');
70500 function hideLayer() {
70501 throttledRedraw.cancel();
70502 layer.transition().duration(250).style('opacity', 0).on('end', layerOff);
70505 function layerOn() {
70506 layer.style('display', 'block');
70509 function layerOff() {
70510 layer.selectAll('.viewfield-group').remove();
70511 layer.style('display', 'none');
70512 } // ensure that all geojson features in a collection have IDs
70515 function ensureIDs(gj) {
70516 if (!gj) return null;
70518 if (gj.type === 'FeatureCollection') {
70519 for (var i = 0; i < gj.features.length; i++) {
70520 ensureFeatureID(gj.features[i]);
70523 ensureFeatureID(gj);
70527 } // ensure that each single Feature object has a unique ID
70530 function ensureFeatureID(feature) {
70531 if (!feature) return;
70532 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
70534 } // Prefer an array of Features instead of a FeatureCollection
70537 function getFeatures(gj) {
70538 if (!gj) return [];
70540 if (gj.type === 'FeatureCollection') {
70541 return gj.features;
70547 function featureKey(d) {
70548 return d.__featurehash__;
70551 function isPolygon(d) {
70552 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
70555 function clipPathID(d) {
70556 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
70559 function featureClasses(d) {
70560 return ['data' + d.__featurehash__, d.geometry.type, isPolygon(d) ? 'area' : '', d.__layerID__ || ''].filter(Boolean).join(' ');
70563 function drawData(selection) {
70564 var vtService = getService();
70565 var getPath = svgPath(projection).geojson;
70566 var getAreaPath = svgPath(projection, null, true).geojson;
70567 var hasData = drawData.hasData();
70568 layer = selection.selectAll('.layer-mapdata').data(_enabled && hasData ? [0] : []);
70569 layer.exit().remove();
70570 layer = layer.enter().append('g').attr('class', 'layer-mapdata').merge(layer);
70571 var surface = context.surface();
70572 if (!surface || surface.empty()) return; // not ready to draw yet, starting up
70575 var geoData, polygonData;
70577 if (_template && vtService) {
70578 // fetch data from vector tile service
70579 var sourceID = _template;
70580 vtService.loadTiles(sourceID, _template, projection);
70581 geoData = vtService.data(sourceID, projection);
70583 geoData = getFeatures(_geojson);
70586 geoData = geoData.filter(getPath);
70587 polygonData = geoData.filter(isPolygon); // Draw clip paths for polygons
70589 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data').data(polygonData, featureKey);
70590 clipPaths.exit().remove();
70591 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-data').attr('id', clipPathID);
70592 clipPathsEnter.append('path');
70593 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', getAreaPath); // Draw fill, shadow, stroke layers
70595 var datagroups = layer.selectAll('g.datagroup').data(['fill', 'shadow', 'stroke']);
70596 datagroups = datagroups.enter().append('g').attr('class', function (d) {
70597 return 'datagroup datagroup-' + d;
70598 }).merge(datagroups); // Draw paths
70605 var paths = datagroups.selectAll('path').data(function (layer) {
70606 return pathData[layer];
70607 }, featureKey); // exit
70609 paths.exit().remove(); // enter/update
70611 paths = paths.enter().append('path').attr('class', function (d) {
70612 var datagroup = this.parentNode.__data__;
70613 return 'pathdata ' + datagroup + ' ' + featureClasses(d);
70614 }).attr('clip-path', function (d) {
70615 var datagroup = this.parentNode.__data__;
70616 return datagroup === 'fill' ? 'url(#' + clipPathID(d) + ')' : null;
70617 }).merge(paths).attr('d', function (d) {
70618 var datagroup = this.parentNode.__data__;
70619 return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
70622 layer.call(drawLabels, 'label-halo', geoData).call(drawLabels, 'label', geoData);
70624 function drawLabels(selection, textClass, data) {
70625 var labelPath = d3_geoPath(projection);
70626 var labelData = data.filter(function (d) {
70627 return _showLabels && d.properties && (d.properties.desc || d.properties.name);
70629 var labels = selection.selectAll('text.' + textClass).data(labelData, featureKey); // exit
70631 labels.exit().remove(); // enter/update
70633 labels = labels.enter().append('text').attr('class', function (d) {
70634 return textClass + ' ' + featureClasses(d);
70635 }).merge(labels).text(function (d) {
70636 return d.properties.desc || d.properties.name;
70637 }).attr('x', function (d) {
70638 var centroid = labelPath.centroid(d);
70639 return centroid[0] + 11;
70640 }).attr('y', function (d) {
70641 var centroid = labelPath.centroid(d);
70642 return centroid[1];
70647 function getExtension(fileName) {
70648 if (!fileName) return;
70649 var re = /\.(gpx|kml|(geo)?json)$/i;
70650 var match = fileName.toLowerCase().match(re);
70651 return match && match.length && match[0];
70654 function xmlToDom(textdata) {
70655 return new DOMParser().parseFromString(textdata, 'text/xml');
70658 drawData.setFile = function (extension, data) {
70665 switch (extension) {
70667 gj = togeojson.gpx(xmlToDom(data));
70671 gj = togeojson.kml(xmlToDom(data));
70676 gj = JSON.parse(data);
70682 if (Object.keys(gj).length) {
70683 _geojson = ensureIDs(gj);
70684 _src = extension + ' data file';
70688 dispatch.call('change');
70692 drawData.showLabels = function (val) {
70693 if (!arguments.length) return _showLabels;
70698 drawData.enabled = function (val) {
70699 if (!arguments.length) return _enabled;
70708 dispatch.call('change');
70712 drawData.hasData = function () {
70713 var gj = _geojson || {};
70714 return !!(_template || Object.keys(gj).length);
70717 drawData.template = function (val, src) {
70718 if (!arguments.length) return _template; // test source against OSM imagery blocklists..
70720 var osm = context.connection();
70723 var blocklists = osm.imageryBlocklists();
70728 for (var i = 0; i < blocklists.length; i++) {
70729 regex = blocklists[i];
70730 fail = regex.test(val);
70733 } // ensure at least one test was run.
70737 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
70738 fail = regex.test(val);
70744 _geojson = null; // strip off the querystring/hash from the template,
70745 // it often includes the access token
70747 _src = src || 'vectortile:' + val.split(/[?#]/)[0];
70748 dispatch.call('change');
70752 drawData.geojson = function (gj, src) {
70753 if (!arguments.length) return _geojson;
70760 if (Object.keys(gj).length) {
70761 _geojson = ensureIDs(gj);
70762 _src = src || 'unknown.geojson';
70765 dispatch.call('change');
70769 drawData.fileList = function (fileList) {
70770 if (!arguments.length) return _fileList;
70772 _fileList = fileList;
70775 if (!fileList || !fileList.length) return this;
70776 var f = fileList[0];
70777 var extension = getExtension(f.name);
70778 var reader = new FileReader();
70780 reader.onload = function () {
70781 return function (e) {
70782 drawData.setFile(extension, e.target.result);
70786 reader.readAsText(f);
70790 drawData.url = function (url, defaultExtension) {
70794 _src = null; // strip off any querystring/hash from the url before checking extension
70796 var testUrl = url.split(/[?#]/)[0];
70797 var extension = getExtension(testUrl) || defaultExtension;
70801 d3_text(url).then(function (data) {
70802 drawData.setFile(extension, data);
70803 })["catch"](function () {
70807 drawData.template(url);
70813 drawData.getSrc = function () {
70817 drawData.fitZoom = function () {
70818 var features = getFeatures(_geojson);
70819 if (!features.length) return;
70820 var map = context.map();
70821 var viewport = map.trimmedExtent().polygon();
70822 var coords = features.reduce(function (coords, feature) {
70823 var geom = feature.geometry;
70824 if (!geom) return coords;
70825 var c = geom.coordinates;
70826 /* eslint-disable no-fallthrough */
70828 switch (geom.type) {
70836 case 'MultiPolygon':
70837 c = utilArrayFlatten(c);
70840 case 'MultiLineString':
70841 c = utilArrayFlatten(c);
70844 /* eslint-enable no-fallthrough */
70847 return utilArrayUnion(coords, c);
70850 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
70851 var extent = geoExtent(d3_geoBounds({
70852 type: 'LineString',
70853 coordinates: coords
70855 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
70865 function svgDebug(projection, context) {
70866 function drawDebug(selection) {
70867 var showTile = context.getDebug('tile');
70868 var showCollision = context.getDebug('collision');
70869 var showImagery = context.getDebug('imagery');
70870 var showTouchTargets = context.getDebug('target');
70871 var showDownloaded = context.getDebug('downloaded');
70872 var debugData = [];
70881 if (showCollision) {
70895 if (showTouchTargets) {
70898 label: 'touchTargets'
70902 if (showDownloaded) {
70905 label: 'downloaded'
70909 var legend = context.container().select('.main-content').selectAll('.debug-legend').data(debugData.length ? [0] : []);
70910 legend.exit().remove();
70911 legend = legend.enter().append('div').attr('class', 'fillD debug-legend').merge(legend);
70912 var legendItems = legend.selectAll('.debug-legend-item').data(debugData, function (d) {
70915 legendItems.exit().remove();
70916 legendItems.enter().append('span').attr('class', function (d) {
70917 return "debug-legend-item ".concat(d["class"]);
70918 }).text(function (d) {
70921 var layer = selection.selectAll('.layer-debug').data(showImagery || showDownloaded ? [0] : []);
70922 layer.exit().remove();
70923 layer = layer.enter().append('g').attr('class', 'layer-debug').merge(layer); // imagery
70925 var extent = context.map().extent();
70926 _mainFileFetcher.get('imagery').then(function (d) {
70927 var hits = showImagery && d.query.bbox(extent.rectangle(), true) || [];
70928 var features = hits.map(function (d) {
70929 return d.features[d.id];
70931 var imagery = layer.selectAll('path.debug-imagery').data(features);
70932 imagery.exit().remove();
70933 imagery.enter().append('path').attr('class', 'debug-imagery debug orange');
70934 })["catch"](function () {
70938 var osm = context.connection();
70939 var dataDownloaded = [];
70941 if (osm && showDownloaded) {
70942 var rtree = osm.caches('get').tile.rtree;
70943 dataDownloaded = rtree.all().map(function (bbox) {
70951 coordinates: [[[bbox.minX, bbox.minY], [bbox.minX, bbox.maxY], [bbox.maxX, bbox.maxY], [bbox.maxX, bbox.minY], [bbox.minX, bbox.minY]]]
70957 var downloaded = layer.selectAll('path.debug-downloaded').data(showDownloaded ? dataDownloaded : []);
70958 downloaded.exit().remove();
70959 downloaded.enter().append('path').attr('class', 'debug-downloaded debug purple'); // update
70961 layer.selectAll('path').attr('d', svgPath(projection).geojson);
70962 } // This looks strange because `enabled` methods on other layers are
70963 // chainable getter/setters, and this one is just a getter.
70966 drawDebug.enabled = function () {
70967 if (!arguments.length) {
70968 return context.getDebug('tile') || context.getDebug('collision') || context.getDebug('imagery') || context.getDebug('target') || context.getDebug('downloaded');
70978 A standalone SVG element that contains only a `defs` sub-element. To be
70979 used once globally, since defs IDs must be unique within a document.
70982 function svgDefs(context) {
70983 var _defsSelection = select(null);
70985 var _spritesheetIds = ['iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite'];
70987 function drawDefs(selection) {
70988 _defsSelection = selection.append('defs'); // add markers
70990 _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
70991 // (they can't inherit it from the line they're attached to),
70992 // so we need to manually define markers for each color of tag
70993 // (also, it's slightly nicer if we can control the
70994 // positioning for different tags)
70997 function addSidedMarker(name, color, offset) {
70998 _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);
71001 addSidedMarker('natural', 'rgb(170, 170, 170)', 0); // for a coastline, the arrows are (somewhat unintuitively) on
71002 // the water side, so let's color them blue (with a gap) to
71003 // give a stronger indication
71005 addSidedMarker('coastline', '#77dede', 1);
71006 addSidedMarker('waterway', '#77dede', 1); // barriers have a dashed line, and separating the triangle
71007 // from the line visually suits that
71009 addSidedMarker('barrier', '#ddd', 1);
71010 addSidedMarker('man_made', '#fff', 0);
71012 _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');
71014 _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
71017 var patterns = _defsSelection.selectAll('pattern').data([// pattern name, pattern image name
71018 ['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) {
71019 return 'ideditor-pattern-' + d[0];
71020 }).attr('width', 32).attr('height', 32).attr('patternUnits', 'userSpaceOnUse');
71022 patterns.append('rect').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('class', function (d) {
71023 return 'pattern-color-' + d[0];
71025 patterns.append('image').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('xlink:href', function (d) {
71026 return context.imagePath('pattern/' + d[1] + '.png');
71027 }); // add clip paths
71029 _defsSelection.selectAll('clipPath').data([12, 18, 20, 32, 45]).enter().append('clipPath').attr('id', function (d) {
71030 return 'ideditor-clip-square-' + d;
71031 }).append('rect').attr('x', 0).attr('y', 0).attr('width', function (d) {
71033 }).attr('height', function (d) {
71035 }); // add symbol spritesheets
71038 addSprites(_spritesheetIds, true);
71041 function addSprites(ids, overrideColors) {
71042 _spritesheetIds = utilArrayUniq(_spritesheetIds.concat(ids));
71044 var spritesheets = _defsSelection.selectAll('.spritesheet').data(_spritesheetIds);
71046 spritesheets.enter().append('g').attr('class', function (d) {
71047 return 'spritesheet spritesheet-' + d;
71048 }).each(function (d) {
71049 var url = context.imagePath(d + '.svg');
71050 var node = select(this).node();
71051 svg(url).then(function (svg) {
71052 node.appendChild(select(svg.documentElement).attr('id', 'ideditor-' + d).node());
71054 if (overrideColors && d !== 'iD-sprite') {
71055 // allow icon colors to be overridden..
71056 select(node).selectAll('path').attr('fill', 'currentColor');
71058 })["catch"](function () {
71062 spritesheets.exit().remove();
71065 drawDefs.addSprites = addSprites;
71069 var _layerEnabled = false;
71073 function svgKeepRight(projection, context, dispatch) {
71074 var throttledRedraw = throttle(function () {
71075 return dispatch.call('change');
71079 var touchLayer = select(null);
71080 var drawLayer = select(null);
71081 var layerVisible = false;
71083 function markerPath(selection, klass) {
71084 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');
71085 } // Loosely-coupled keepRight service for fetching issues.
71088 function getService() {
71089 if (services.keepRight && !_qaService) {
71090 _qaService = services.keepRight;
71092 _qaService.on('loaded', throttledRedraw);
71093 } else if (!services.keepRight && _qaService) {
71098 } // Show the markers
71101 function editOn() {
71102 if (!layerVisible) {
71103 layerVisible = true;
71104 drawLayer.style('display', 'block');
71106 } // Immediately remove the markers and their touch targets
71109 function editOff() {
71110 if (layerVisible) {
71111 layerVisible = false;
71112 drawLayer.style('display', 'none');
71113 drawLayer.selectAll('.qaItem.keepRight').remove();
71114 touchLayer.selectAll('.qaItem.keepRight').remove();
71116 } // Enable the layer. This shows the markers and transitions them to visible.
71119 function layerOn() {
71121 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
71122 return dispatch.call('change');
71124 } // Disable the layer. This transitions the layer invisible and then hides the markers.
71127 function layerOff() {
71128 throttledRedraw.cancel();
71129 drawLayer.interrupt();
71130 touchLayer.selectAll('.qaItem.keepRight').remove();
71131 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
71133 dispatch.call('change');
71135 } // Update the issue markers
71138 function updateMarkers() {
71139 if (!layerVisible || !_layerEnabled) return;
71140 var service = getService();
71141 var selectedID = context.selectedErrorID();
71142 var data = service ? service.getItems(projection) : [];
71143 var getTransform = svgPointTransform(projection); // Draw markers..
71145 var markers = drawLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
71149 markers.exit().remove(); // enter
71151 var markersEnter = markers.enter().append('g').attr('class', function (d) {
71152 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
71154 markersEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
71155 markersEnter.append('path').call(markerPath, 'shadow');
71156 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
71158 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
71159 return d.id === selectedID;
71160 }).attr('transform', getTransform); // Draw targets..
71162 if (touchLayer.empty()) return;
71163 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71164 var targets = touchLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
71168 targets.exit().remove(); // enter/update
71170 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
71171 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
71172 }).attr('transform', getTransform);
71174 function sortY(a, b) {
71175 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];
71177 } // Draw the keepRight layer and schedule loading issues and updating markers.
71180 function drawKeepRight(selection) {
71181 var service = getService();
71182 var surface = context.surface();
71184 if (surface && !surface.empty()) {
71185 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71188 drawLayer = selection.selectAll('.layer-keepRight').data(service ? [0] : []);
71189 drawLayer.exit().remove();
71190 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-keepRight').style('display', _layerEnabled ? 'block' : 'none').merge(drawLayer);
71192 if (_layerEnabled) {
71193 if (service && ~~context.map().zoom() >= minZoom) {
71195 service.loadIssues(projection);
71201 } // Toggles the layer on and off
71204 drawKeepRight.enabled = function (val) {
71205 if (!arguments.length) return _layerEnabled;
71206 _layerEnabled = val;
71208 if (_layerEnabled) {
71213 if (context.selectedErrorID()) {
71214 context.enter(modeBrowse(context));
71218 dispatch.call('change');
71222 drawKeepRight.supported = function () {
71223 return !!getService();
71226 return drawKeepRight;
71229 function svgGeolocate(projection) {
71230 var layer = select(null);
71235 if (svgGeolocate.initialized) return; // run once
71237 svgGeolocate.enabled = false;
71238 svgGeolocate.initialized = true;
71241 function showLayer() {
71242 layer.style('display', 'block');
71245 function hideLayer() {
71246 layer.transition().duration(250).style('opacity', 0);
71249 function layerOn() {
71250 layer.style('opacity', 0).transition().duration(250).style('opacity', 1);
71253 function layerOff() {
71254 layer.style('display', 'none');
71257 function transform(d) {
71258 return svgPointTransform(projection)(d);
71261 function accuracy(accuracy, loc) {
71262 // converts accuracy to pixels...
71263 var degreesRadius = geoMetersToLat(accuracy),
71264 tangentLoc = [loc[0], loc[1] + degreesRadius],
71265 projectedTangent = projection(tangentLoc),
71266 projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value...
71268 return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
71271 function update() {
71272 var geolocation = {
71273 loc: [_position.coords.longitude, _position.coords.latitude]
71275 var groups = layer.selectAll('.geolocations').selectAll('.geolocation').data([geolocation]);
71276 groups.exit().remove();
71277 var pointsEnter = groups.enter().append('g').attr('class', 'geolocation');
71278 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');
71279 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');
71280 groups.merge(pointsEnter).attr('transform', transform);
71281 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
71284 function drawLocation(selection) {
71285 var enabled = svgGeolocate.enabled;
71286 layer = selection.selectAll('.layer-geolocate').data([0]);
71287 layer.exit().remove();
71288 var layerEnter = layer.enter().append('g').attr('class', 'layer-geolocate').style('display', enabled ? 'block' : 'none');
71289 layerEnter.append('g').attr('class', 'geolocations');
71290 layer = layerEnter.merge(layer);
71299 drawLocation.enabled = function (position, enabled) {
71300 if (!arguments.length) return svgGeolocate.enabled;
71301 _position = position;
71302 svgGeolocate.enabled = enabled;
71304 if (svgGeolocate.enabled) {
71315 return drawLocation;
71318 function svgLabels(projection, context) {
71319 var path = d3_geoPath(projection);
71320 var detected = utilDetect();
71321 var baselineHack = detected.ie || detected.browser.toLowerCase() === 'edge' || detected.browser.toLowerCase() === 'firefox' && detected.version >= 70;
71323 var _rdrawn = new RBush();
71325 var _rskipped = new RBush();
71327 var _textWidthCache = {};
71328 var _entitybboxes = {}; // Listed from highest to lowest priority
71330 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]];
71332 function shouldSkipIcon(preset) {
71333 var noIcons = ['building', 'landuse', 'natural'];
71334 return noIcons.some(function (s) {
71335 return preset.id.indexOf(s) >= 0;
71339 function get(array, prop) {
71340 return function (d, i) {
71341 return array[i][prop];
71345 function textWidth(text, size, elem) {
71346 var c = _textWidthCache[size];
71347 if (!c) c = _textWidthCache[size] = {};
71352 c[text] = elem.getComputedTextLength();
71355 var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
71357 if (str === null) {
71358 return size / 3 * 2 * text.length;
71360 return size / 3 * (2 * text.length + str.length);
71365 function drawLinePaths(selection, entities, filter, classes, labels) {
71366 var paths = selection.selectAll('path').filter(filter).data(entities, osmEntity.key); // exit
71368 paths.exit().remove(); // enter/update
71370 paths.enter().append('path').style('stroke-width', get(labels, 'font-size')).attr('id', function (d) {
71371 return 'ideditor-labelpath-' + d.id;
71372 }).attr('class', classes).merge(paths).attr('d', get(labels, 'lineString'));
71375 function drawLineLabels(selection, entities, filter, classes, labels) {
71376 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71378 texts.exit().remove(); // enter
71380 texts.enter().append('text').attr('class', function (d, i) {
71381 return classes + ' ' + labels[i].classes + ' ' + d.id;
71382 }).attr('dy', baselineHack ? '0.35em' : null).append('textPath').attr('class', 'textpath'); // update
71384 selection.selectAll('text.' + classes).selectAll('.textpath').filter(filter).data(entities, osmEntity.key).attr('startOffset', '50%').attr('xlink:href', function (d) {
71385 return '#ideditor-labelpath-' + d.id;
71386 }).text(utilDisplayNameForPath);
71389 function drawPointLabels(selection, entities, filter, classes, labels) {
71390 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71392 texts.exit().remove(); // enter/update
71394 texts.enter().append('text').attr('class', function (d, i) {
71395 return classes + ' ' + labels[i].classes + ' ' + d.id;
71396 }).merge(texts).attr('x', get(labels, 'x')).attr('y', get(labels, 'y')).style('text-anchor', get(labels, 'textAnchor')).text(utilDisplayName).each(function (d, i) {
71397 textWidth(utilDisplayName(d), labels[i].height, this);
71401 function drawAreaLabels(selection, entities, filter, classes, labels) {
71402 entities = entities.filter(hasText);
71403 labels = labels.filter(hasText);
71404 drawPointLabels(selection, entities, filter, classes, labels);
71406 function hasText(d, i) {
71407 return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
71411 function drawAreaIcons(selection, entities, filter, classes, labels) {
71412 var icons = selection.selectAll('use.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71414 icons.exit().remove(); // enter/update
71416 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) {
71417 var preset = _mainPresetIndex.match(d, context.graph());
71418 var picon = preset && preset.icon;
71423 var isMaki = /^maki-/.test(picon);
71424 return '#' + picon + (isMaki ? '-15' : '');
71429 function drawCollisionBoxes(selection, rtree, which) {
71430 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
71433 if (context.getDebug('collision')) {
71434 gj = rtree.all().map(function (d) {
71437 coordinates: [[[d.minX, d.minY], [d.maxX, d.minY], [d.maxX, d.maxY], [d.minX, d.maxY], [d.minX, d.minY]]]
71442 var boxes = selection.selectAll('.' + which).data(gj); // exit
71444 boxes.exit().remove(); // enter/update
71446 boxes.enter().append('path').attr('class', classes).merge(boxes).attr('d', d3_geoPath());
71449 function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
71450 var wireframe = context.surface().classed('fill-wireframe');
71451 var zoom = geoScaleToZoom(projection.scale());
71452 var labelable = [];
71453 var renderNodeAs = {};
71454 var i, j, k, entity, geometry;
71456 for (i = 0; i < labelStack.length; i++) {
71457 labelable.push([]);
71465 _entitybboxes = {};
71467 for (i = 0; i < entities.length; i++) {
71468 entity = entities[i];
71469 var toRemove = [].concat(_entitybboxes[entity.id] || []).concat(_entitybboxes[entity.id + 'I'] || []);
71471 for (j = 0; j < toRemove.length; j++) {
71472 _rdrawn.remove(toRemove[j]);
71474 _rskipped.remove(toRemove[j]);
71477 } // Loop through all the entities to do some preprocessing
71480 for (i = 0; i < entities.length; i++) {
71481 entity = entities[i];
71482 geometry = entity.geometry(graph); // Insert collision boxes around interesting points/vertices
71484 if (geometry === 'point' || geometry === 'vertex' && isInterestingVertex(entity)) {
71485 var hasDirections = entity.directions(graph, projection).length;
71488 if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
71489 renderNodeAs[entity.id] = 'point';
71490 markerPadding = 20; // extra y for marker height
71492 renderNodeAs[entity.id] = 'vertex';
71496 var coord = projection(entity.loc);
71497 var nodePadding = 10;
71499 minX: coord[0] - nodePadding,
71500 minY: coord[1] - nodePadding - markerPadding,
71501 maxX: coord[0] + nodePadding,
71502 maxY: coord[1] + nodePadding
71504 doInsert(bbox, entity.id + 'P');
71505 } // From here on, treat vertices like points
71508 if (geometry === 'vertex') {
71509 geometry = 'point';
71510 } // Determine which entities are label-able
71513 var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
71514 var icon = preset && !shouldSkipIcon(preset) && preset.icon;
71515 if (!icon && !utilDisplayName(entity)) continue;
71517 for (k = 0; k < labelStack.length; k++) {
71518 var matchGeom = labelStack[k][0];
71519 var matchKey = labelStack[k][1];
71520 var matchVal = labelStack[k][2];
71521 var hasVal = entity.tags[matchKey];
71523 if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
71524 labelable[k].push(entity);
71539 }; // Try and find a valid label for labellable entities
71541 for (k = 0; k < labelable.length; k++) {
71542 var fontSize = labelStack[k][3];
71544 for (i = 0; i < labelable[k].length; i++) {
71545 entity = labelable[k][i];
71546 geometry = entity.geometry(graph);
71547 var getName = geometry === 'line' ? utilDisplayNameForPath : utilDisplayName;
71548 var name = getName(entity);
71549 var width = name && textWidth(name, fontSize);
71552 if (geometry === 'point' || geometry === 'vertex') {
71553 // no point or vertex labels in wireframe mode
71554 // no vertex labels at low zooms (vertices have no icons)
71555 if (wireframe) continue;
71556 var renderAs = renderNodeAs[entity.id];
71557 if (renderAs === 'vertex' && zoom < 17) continue;
71558 p = getPointLabel(entity, width, fontSize, renderAs);
71559 } else if (geometry === 'line') {
71560 p = getLineLabel(entity, width, fontSize);
71561 } else if (geometry === 'area') {
71562 p = getAreaLabel(entity, width, fontSize);
71566 if (geometry === 'vertex') {
71567 geometry = 'point';
71568 } // treat vertex like point
71571 p.classes = geometry + ' tag-' + labelStack[k][1];
71572 positions[geometry].push(p);
71573 labelled[geometry].push(entity);
71578 function isInterestingVertex(entity) {
71579 var selectedIDs = context.selectedIDs();
71580 return entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || selectedIDs.indexOf(entity.id) !== -1 || graph.parentWays(entity).some(function (parent) {
71581 return selectedIDs.indexOf(parent.id) !== -1;
71585 function getPointLabel(entity, width, height, geometry) {
71586 var y = geometry === 'point' ? -12 : 0;
71587 var pointOffsets = {
71588 ltr: [15, y, 'start'],
71589 rtl: [-15, y, 'end']
71591 var textDirection = _mainLocalizer.textDirection();
71592 var coord = projection(entity.loc);
71593 var textPadding = 2;
71594 var offset = pointOffsets[textDirection];
71598 x: coord[0] + offset[0],
71599 y: coord[1] + offset[1],
71600 textAnchor: offset[2]
71601 }; // insert a collision box for the text label..
71605 if (textDirection === 'rtl') {
71607 minX: p.x - width - textPadding,
71608 minY: p.y - height / 2 - textPadding,
71609 maxX: p.x + textPadding,
71610 maxY: p.y + height / 2 + textPadding
71614 minX: p.x - textPadding,
71615 minY: p.y - height / 2 - textPadding,
71616 maxX: p.x + width + textPadding,
71617 maxY: p.y + height / 2 + textPadding
71621 if (tryInsert([bbox], entity.id, true)) {
71626 function getLineLabel(entity, width, height) {
71627 var viewport = geoExtent(context.projection.clipExtent()).polygon();
71628 var points = graph.childNodes(entity).map(function (node) {
71629 return projection(node.loc);
71631 var length = geoPathLength(points);
71632 if (length < width + 20) return; // % along the line to attempt to place the label
71634 var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
71637 for (var i = 0; i < lineOffsets.length; i++) {
71638 var offset = lineOffsets[i];
71639 var middle = offset / 100 * length;
71640 var start = middle - width / 2;
71641 if (start < 0 || start + width > length) continue; // generate subpath and ignore paths that are invalid or don't cross viewport.
71643 var sub = subpath(points, start, start + width);
71645 if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
71649 var isReverse = reverse(sub);
71652 sub = sub.reverse();
71656 var boxsize = (height + 2) / 2;
71658 for (var j = 0; j < sub.length - 1; j++) {
71660 var b = sub[j + 1]; // split up the text into small collision boxes
71662 var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
71664 for (var box = 0; box < num; box++) {
71665 var p = geoVecInterp(a, b, box / num);
71666 var x0 = p[0] - boxsize - padding;
71667 var y0 = p[1] - boxsize - padding;
71668 var x1 = p[0] + boxsize + padding;
71669 var y1 = p[1] + boxsize + padding;
71671 minX: Math.min(x0, x1),
71672 minY: Math.min(y0, y1),
71673 maxX: Math.max(x0, x1),
71674 maxY: Math.max(y0, y1)
71679 if (tryInsert(bboxes, entity.id, false)) {
71682 'font-size': height + 2,
71683 lineString: lineString(sub),
71684 startOffset: offset + '%'
71689 function reverse(p) {
71690 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
71691 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI / 2 && angle > -Math.PI / 2);
71694 function lineString(points) {
71695 return 'M' + points.join('L');
71698 function subpath(points, from, to) {
71700 var start, end, i0, i1;
71702 for (var i = 0; i < points.length - 1; i++) {
71704 var b = points[i + 1];
71705 var current = geoVecLength(a, b);
71708 if (!start && sofar + current >= from) {
71709 portion = (from - sofar) / current;
71710 start = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
71714 if (!end && sofar + current >= to) {
71715 portion = (to - sofar) / current;
71716 end = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
71723 var result = points.slice(i0, i1);
71724 result.unshift(start);
71730 function getAreaLabel(entity, width, height) {
71731 var centroid = path.centroid(entity.asGeoJSON(graph, true));
71732 var extent = entity.extent(graph);
71733 var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
71734 if (isNaN(centroid[0]) || areaWidth < 20) return;
71735 var preset = _mainPresetIndex.match(entity, context.graph());
71736 var picon = preset && preset.icon;
71742 // icon and label..
71744 addLabel(iconSize + padding);
71754 function addIcon() {
71755 var iconX = centroid[0] - iconSize / 2;
71756 var iconY = centroid[1] - iconSize / 2;
71760 maxX: iconX + iconSize,
71761 maxY: iconY + iconSize
71764 if (tryInsert([bbox], entity.id + 'I', true)) {
71765 p.transform = 'translate(' + iconX + ',' + iconY + ')';
71772 function addLabel(yOffset) {
71773 if (width && areaWidth >= width + 20) {
71774 var labelX = centroid[0];
71775 var labelY = centroid[1] + yOffset;
71777 minX: labelX - width / 2 - padding,
71778 minY: labelY - height / 2 - padding,
71779 maxX: labelX + width / 2 + padding,
71780 maxY: labelY + height / 2 + padding
71783 if (tryInsert([bbox], entity.id, true)) {
71786 p.textAnchor = 'middle';
71794 } // force insert a singular bounding box
71795 // singular box only, no array, id better be unique
71798 function doInsert(bbox, id) {
71800 var oldbox = _entitybboxes[id];
71803 _rdrawn.remove(oldbox);
71806 _entitybboxes[id] = bbox;
71808 _rdrawn.insert(bbox);
71811 function tryInsert(bboxes, id, saveSkipped) {
71812 var skipped = false;
71814 for (var i = 0; i < bboxes.length; i++) {
71815 var bbox = bboxes[i];
71816 bbox.id = id; // Check that label is visible
71818 if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
71823 if (_rdrawn.collides(bbox)) {
71829 _entitybboxes[id] = bboxes;
71833 _rskipped.load(bboxes);
71836 _rdrawn.load(bboxes);
71842 var layer = selection.selectAll('.layer-osm.labels');
71843 layer.selectAll('.labels-group').data(['halo', 'label', 'debug']).enter().append('g').attr('class', function (d) {
71844 return 'labels-group ' + d;
71846 var halo = layer.selectAll('.labels-group.halo');
71847 var label = layer.selectAll('.labels-group.label');
71848 var debug = layer.selectAll('.labels-group.debug'); // points
71850 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
71851 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); // lines
71853 drawLinePaths(layer, labelled.line, filter, '', positions.line);
71854 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
71855 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); // areas
71857 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
71858 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
71859 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
71860 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); // debug
71862 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
71863 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
71864 layer.call(filterLabels);
71867 function filterLabels(selection) {
71868 var drawLayer = selection.selectAll('.layer-osm.labels');
71869 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
71870 layers.selectAll('.nolabel').classed('nolabel', false);
71871 var mouse = context.map().mouse();
71872 var graph = context.graph();
71873 var selectedIDs = context.selectedIDs();
71875 var pad, bbox; // hide labels near the mouse
71880 minX: mouse[0] - pad,
71881 minY: mouse[1] - pad,
71882 maxX: mouse[0] + pad,
71883 maxY: mouse[1] + pad
71886 var nearMouse = _rdrawn.search(bbox).map(function (entity) {
71890 ids.push.apply(ids, nearMouse);
71891 } // hide labels on selected nodes (they look weird when dragging / haloed)
71894 for (var i = 0; i < selectedIDs.length; i++) {
71895 var entity = graph.hasEntity(selectedIDs[i]);
71897 if (entity && entity.type === 'node') {
71898 ids.push(selectedIDs[i]);
71902 layers.selectAll(utilEntitySelector(ids)).classed('nolabel', true); // draw the mouse bbox if debugging is on..
71904 var debug = selection.selectAll('.labels-group.debug');
71907 if (context.getDebug('collision')) {
71910 coordinates: [[[bbox.minX, bbox.minY], [bbox.maxX, bbox.minY], [bbox.maxX, bbox.maxY], [bbox.minX, bbox.maxY], [bbox.minX, bbox.minY]]]
71914 var box = debug.selectAll('.debug-mouse').data(gj); // exit
71916 box.exit().remove(); // enter/update
71918 box.enter().append('path').attr('class', 'debug debug-mouse yellow').merge(box).attr('d', d3_geoPath());
71921 var throttleFilterLabels = throttle(filterLabels, 100);
71923 drawLabels.observe = function (selection) {
71924 var listener = function listener() {
71925 throttleFilterLabels(selection);
71928 selection.on('mousemove.hidelabels', listener);
71929 context.on('enter.hidelabels', listener);
71932 drawLabels.off = function (selection) {
71933 throttleFilterLabels.cancel();
71934 selection.on('mousemove.hidelabels', null);
71935 context.on('enter.hidelabels', null);
71941 var _layerEnabled$1 = false;
71945 function svgImproveOSM(projection, context, dispatch) {
71946 var throttledRedraw = throttle(function () {
71947 return dispatch.call('change');
71951 var touchLayer = select(null);
71952 var drawLayer = select(null);
71953 var layerVisible = false;
71955 function markerPath(selection, klass) {
71956 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');
71957 } // Loosely-coupled improveOSM service for fetching issues
71960 function getService() {
71961 if (services.improveOSM && !_qaService$1) {
71962 _qaService$1 = services.improveOSM;
71964 _qaService$1.on('loaded', throttledRedraw);
71965 } else if (!services.improveOSM && _qaService$1) {
71966 _qaService$1 = null;
71969 return _qaService$1;
71970 } // Show the markers
71973 function editOn() {
71974 if (!layerVisible) {
71975 layerVisible = true;
71976 drawLayer.style('display', 'block');
71978 } // Immediately remove the markers and their touch targets
71981 function editOff() {
71982 if (layerVisible) {
71983 layerVisible = false;
71984 drawLayer.style('display', 'none');
71985 drawLayer.selectAll('.qaItem.improveOSM').remove();
71986 touchLayer.selectAll('.qaItem.improveOSM').remove();
71988 } // Enable the layer. This shows the markers and transitions them to visible.
71991 function layerOn() {
71993 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
71994 return dispatch.call('change');
71996 } // Disable the layer. This transitions the layer invisible and then hides the markers.
71999 function layerOff() {
72000 throttledRedraw.cancel();
72001 drawLayer.interrupt();
72002 touchLayer.selectAll('.qaItem.improveOSM').remove();
72003 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
72005 dispatch.call('change');
72007 } // Update the issue markers
72010 function updateMarkers() {
72011 if (!layerVisible || !_layerEnabled$1) return;
72012 var service = getService();
72013 var selectedID = context.selectedErrorID();
72014 var data = service ? service.getItems(projection) : [];
72015 var getTransform = svgPointTransform(projection); // Draw markers..
72017 var markers = drawLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
72021 markers.exit().remove(); // enter
72023 var markersEnter = markers.enter().append('g').attr('class', function (d) {
72024 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
72026 markersEnter.append('polygon').call(markerPath, 'shadow');
72027 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
72028 markersEnter.append('polygon').attr('fill', 'currentColor').call(markerPath, 'qaItem-fill');
72029 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
72030 var picon = d.icon;
72035 var isMaki = /^maki-/.test(picon);
72036 return "#".concat(picon).concat(isMaki ? '-11' : '');
72040 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
72041 return d.id === selectedID;
72042 }).attr('transform', getTransform); // Draw targets..
72044 if (touchLayer.empty()) return;
72045 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72046 var targets = touchLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
72050 targets.exit().remove(); // enter/update
72052 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
72053 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
72054 }).attr('transform', getTransform);
72056 function sortY(a, b) {
72057 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
72059 } // Draw the ImproveOSM layer and schedule loading issues and updating markers.
72062 function drawImproveOSM(selection) {
72063 var service = getService();
72064 var surface = context.surface();
72066 if (surface && !surface.empty()) {
72067 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
72070 drawLayer = selection.selectAll('.layer-improveOSM').data(service ? [0] : []);
72071 drawLayer.exit().remove();
72072 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-improveOSM').style('display', _layerEnabled$1 ? 'block' : 'none').merge(drawLayer);
72074 if (_layerEnabled$1) {
72075 if (service && ~~context.map().zoom() >= minZoom) {
72077 service.loadIssues(projection);
72083 } // Toggles the layer on and off
72086 drawImproveOSM.enabled = function (val) {
72087 if (!arguments.length) return _layerEnabled$1;
72088 _layerEnabled$1 = val;
72090 if (_layerEnabled$1) {
72095 if (context.selectedErrorID()) {
72096 context.enter(modeBrowse(context));
72100 dispatch.call('change');
72104 drawImproveOSM.supported = function () {
72105 return !!getService();
72108 return drawImproveOSM;
72111 var _layerEnabled$2 = false;
72115 function svgOsmose(projection, context, dispatch) {
72116 var throttledRedraw = throttle(function () {
72117 return dispatch.call('change');
72121 var touchLayer = select(null);
72122 var drawLayer = select(null);
72123 var layerVisible = false;
72125 function markerPath(selection, klass) {
72126 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');
72127 } // Loosely-coupled osmose service for fetching issues
72130 function getService() {
72131 if (services.osmose && !_qaService$2) {
72132 _qaService$2 = services.osmose;
72134 _qaService$2.on('loaded', throttledRedraw);
72135 } else if (!services.osmose && _qaService$2) {
72136 _qaService$2 = null;
72139 return _qaService$2;
72140 } // Show the markers
72143 function editOn() {
72144 if (!layerVisible) {
72145 layerVisible = true;
72146 drawLayer.style('display', 'block');
72148 } // Immediately remove the markers and their touch targets
72151 function editOff() {
72152 if (layerVisible) {
72153 layerVisible = false;
72154 drawLayer.style('display', 'none');
72155 drawLayer.selectAll('.qaItem.osmose').remove();
72156 touchLayer.selectAll('.qaItem.osmose').remove();
72158 } // Enable the layer. This shows the markers and transitions them to visible.
72161 function layerOn() {
72163 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
72164 return dispatch.call('change');
72166 } // Disable the layer. This transitions the layer invisible and then hides the markers.
72169 function layerOff() {
72170 throttledRedraw.cancel();
72171 drawLayer.interrupt();
72172 touchLayer.selectAll('.qaItem.osmose').remove();
72173 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
72175 dispatch.call('change');
72177 } // Update the issue markers
72180 function updateMarkers() {
72181 if (!layerVisible || !_layerEnabled$2) return;
72182 var service = getService();
72183 var selectedID = context.selectedErrorID();
72184 var data = service ? service.getItems(projection) : [];
72185 var getTransform = svgPointTransform(projection); // Draw markers..
72187 var markers = drawLayer.selectAll('.qaItem.osmose').data(data, function (d) {
72191 markers.exit().remove(); // enter
72193 var markersEnter = markers.enter().append('g').attr('class', function (d) {
72194 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
72196 markersEnter.append('polygon').call(markerPath, 'shadow');
72197 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
72198 markersEnter.append('polygon').attr('fill', function (d) {
72199 return service.getColor(d.item);
72200 }).call(markerPath, 'qaItem-fill');
72201 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
72202 var picon = d.icon;
72207 var isMaki = /^maki-/.test(picon);
72208 return "#".concat(picon).concat(isMaki ? '-11' : '');
72212 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
72213 return d.id === selectedID;
72214 }).attr('transform', getTransform); // Draw targets..
72216 if (touchLayer.empty()) return;
72217 var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
72218 var targets = touchLayer.selectAll('.qaItem.osmose').data(data, function (d) {
72222 targets.exit().remove(); // enter/update
72224 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
72225 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
72226 }).attr('transform', getTransform);
72228 function sortY(a, b) {
72229 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
72231 } // Draw the Osmose layer and schedule loading issues and updating markers.
72234 function drawOsmose(selection) {
72235 var service = getService();
72236 var surface = context.surface();
72238 if (surface && !surface.empty()) {
72239 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
72242 drawLayer = selection.selectAll('.layer-osmose').data(service ? [0] : []);
72243 drawLayer.exit().remove();
72244 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-osmose').style('display', _layerEnabled$2 ? 'block' : 'none').merge(drawLayer);
72246 if (_layerEnabled$2) {
72247 if (service && ~~context.map().zoom() >= minZoom) {
72249 service.loadIssues(projection);
72255 } // Toggles the layer on and off
72258 drawOsmose.enabled = function (val) {
72259 if (!arguments.length) return _layerEnabled$2;
72260 _layerEnabled$2 = val;
72262 if (_layerEnabled$2) {
72263 // Strings supplied by Osmose fetched before showing layer for first time
72264 // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
72265 // Also, If layer is toggled quickly multiple requests are sent
72266 getService().loadStrings().then(layerOn)["catch"](function (err) {
72267 console.log(err); // eslint-disable-line no-console
72272 if (context.selectedErrorID()) {
72273 context.enter(modeBrowse(context));
72277 dispatch.call('change');
72281 drawOsmose.supported = function () {
72282 return !!getService();
72288 function svgStreetside(projection, context, dispatch) {
72289 var throttledRedraw = throttle(function () {
72290 dispatch.call('change');
72294 var minMarkerZoom = 16;
72295 var minViewfieldZoom = 18;
72296 var layer = select(null);
72297 var _viewerYaw = 0;
72298 var _selectedSequence = null;
72307 if (svgStreetside.initialized) return; // run once
72309 svgStreetside.enabled = false;
72310 svgStreetside.initialized = true;
72317 function getService() {
72318 if (services.streetside && !_streetside) {
72319 _streetside = services.streetside;
72321 _streetside.event.on('viewerChanged.svgStreetside', viewerChanged).on('loadedImages.svgStreetside', throttledRedraw);
72322 } else if (!services.streetside && _streetside) {
72323 _streetside = null;
72326 return _streetside;
72333 function showLayer() {
72334 var service = getService();
72335 if (!service) return;
72337 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
72338 dispatch.call('change');
72346 function hideLayer() {
72347 throttledRedraw.cancel();
72348 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
72355 function editOn() {
72356 layer.style('display', 'block');
72363 function editOff() {
72364 layer.selectAll('.viewfield-group').remove();
72365 layer.style('display', 'none');
72368 * click() Handles 'bubble' point click event.
72372 function click(d3_event, d) {
72373 var service = getService();
72374 if (!service) return; // try to preserve the viewer rotation when staying on the same sequence
72376 if (d.sequenceKey !== _selectedSequence) {
72377 _viewerYaw = 0; // reset
72380 _selectedSequence = d.sequenceKey;
72381 service.ensureViewerLoaded(context).then(function () {
72382 service.selectImage(context, d.key).yaw(_viewerYaw).showViewer(context);
72384 context.map().centerEase(d.loc);
72391 function mouseover(d3_event, d) {
72392 var service = getService();
72393 if (service) service.setStyles(context, d);
72400 function mouseout() {
72401 var service = getService();
72402 if (service) service.setStyles(context, null);
72409 function transform(d) {
72410 var t = svgPointTransform(projection)(d);
72411 var rot = d.ca + _viewerYaw;
72414 t += ' rotate(' + Math.floor(rot) + ',0,0)';
72420 function viewerChanged() {
72421 var service = getService();
72422 if (!service) return;
72423 var viewer = service.viewer();
72424 if (!viewer) return; // update viewfield rotation
72426 _viewerYaw = viewer.getYaw(); // avoid updating if the map is currently transformed
72427 // e.g. during drags or easing.
72429 if (context.map().isTransformed()) return;
72430 layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
72433 context.photos().on('change.streetside', update);
72435 function filterBubbles(bubbles) {
72436 var fromDate = context.photos().fromDate();
72437 var toDate = context.photos().toDate();
72438 var usernames = context.photos().usernames();
72441 var fromTimestamp = new Date(fromDate).getTime();
72442 bubbles = bubbles.filter(function (bubble) {
72443 return new Date(bubble.captured_at).getTime() >= fromTimestamp;
72448 var toTimestamp = new Date(toDate).getTime();
72449 bubbles = bubbles.filter(function (bubble) {
72450 return new Date(bubble.captured_at).getTime() <= toTimestamp;
72455 bubbles = bubbles.filter(function (bubble) {
72456 return usernames.indexOf(bubble.captured_by) !== -1;
72463 function filterSequences(sequences) {
72464 var fromDate = context.photos().fromDate();
72465 var toDate = context.photos().toDate();
72466 var usernames = context.photos().usernames();
72469 var fromTimestamp = new Date(fromDate).getTime();
72470 sequences = sequences.filter(function (sequences) {
72471 return new Date(sequences.properties.captured_at).getTime() >= fromTimestamp;
72476 var toTimestamp = new Date(toDate).getTime();
72477 sequences = sequences.filter(function (sequences) {
72478 return new Date(sequences.properties.captured_at).getTime() <= toTimestamp;
72483 sequences = sequences.filter(function (sequences) {
72484 return usernames.indexOf(sequences.properties.captured_by) !== -1;
72495 function update() {
72496 var viewer = context.container().select('.photoviewer');
72497 var selected = viewer.empty() ? undefined : viewer.datum();
72498 var z = ~~context.map().zoom();
72499 var showMarkers = z >= minMarkerZoom;
72500 var showViewfields = z >= minViewfieldZoom;
72501 var service = getService();
72502 var sequences = [];
72505 if (context.photos().showsPanoramic()) {
72506 sequences = service ? service.sequences(projection) : [];
72507 bubbles = service && showMarkers ? service.bubbles(projection) : [];
72508 sequences = filterSequences(sequences);
72509 bubbles = filterBubbles(bubbles);
72512 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
72513 return d.properties.key;
72516 traces.exit().remove(); // enter/update
72518 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
72519 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(bubbles, function (d) {
72520 // force reenter once bubbles are attached to a sequence
72521 return d.key + (d.sequenceKey ? 'v1' : 'v0');
72524 groups.exit().remove(); // enter
72526 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
72527 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72529 var markers = groups.merge(groupsEnter).sort(function (a, b) {
72530 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1];
72531 }).attr('transform', transform).select('.viewfield-scale');
72532 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72533 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72534 viewfields.exit().remove(); // viewfields may or may not be drawn...
72535 // but if they are, draw below the circles
72537 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
72539 function viewfieldPath() {
72540 var d = this.parentNode.__data__;
72543 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
72545 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
72551 * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
72552 * 'svgStreetside()' is called from index.js
72556 function drawImages(selection) {
72557 var enabled = svgStreetside.enabled;
72558 var service = getService();
72559 layer = selection.selectAll('.layer-streetside-images').data(service ? [0] : []);
72560 layer.exit().remove();
72561 var layerEnter = layer.enter().append('g').attr('class', 'layer-streetside-images').style('display', enabled ? 'block' : 'none');
72562 layerEnter.append('g').attr('class', 'sequences');
72563 layerEnter.append('g').attr('class', 'markers');
72564 layer = layerEnter.merge(layer);
72567 if (service && ~~context.map().zoom() >= minZoom) {
72570 service.loadBubbles(projection);
72577 * drawImages.enabled().
72581 drawImages.enabled = function (_) {
72582 if (!arguments.length) return svgStreetside.enabled;
72583 svgStreetside.enabled = _;
72585 if (svgStreetside.enabled) {
72591 dispatch.call('change');
72595 * drawImages.supported().
72599 drawImages.supported = function () {
72600 return !!getService();
72607 function svgMapillaryImages(projection, context, dispatch) {
72608 var throttledRedraw = throttle(function () {
72609 dispatch.call('change');
72613 var minMarkerZoom = 16;
72614 var minViewfieldZoom = 18;
72615 var layer = select(null);
72619 var viewerCompassAngle;
72622 if (svgMapillaryImages.initialized) return; // run once
72624 svgMapillaryImages.enabled = false;
72625 svgMapillaryImages.initialized = true;
72628 function getService() {
72629 if (services.mapillary && !_mapillary) {
72630 _mapillary = services.mapillary;
72632 _mapillary.event.on('loadedImages', throttledRedraw);
72633 } else if (!services.mapillary && _mapillary) {
72640 function showLayer() {
72641 var service = getService();
72642 if (!service) return;
72644 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
72645 dispatch.call('change');
72649 function hideLayer() {
72650 throttledRedraw.cancel();
72651 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
72654 function editOn() {
72655 layer.style('display', 'block');
72658 function editOff() {
72659 layer.selectAll('.viewfield-group').remove();
72660 layer.style('display', 'none');
72663 function click(d3_event, d) {
72664 var service = getService();
72665 if (!service) return;
72666 service.ensureViewerLoaded(context).then(function () {
72667 service.selectImage(context, d.key).showViewer(context);
72669 context.map().centerEase(d.loc);
72672 function mouseover(d) {
72673 var service = getService();
72674 if (service) service.setStyles(context, d);
72677 function mouseout() {
72678 var service = getService();
72679 if (service) service.setStyles(context, null);
72682 function transform(d) {
72683 var t = svgPointTransform(projection)(d);
72685 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
72686 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
72688 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
72694 context.photos().on('change.mapillary_images', update);
72696 function filterImages(images) {
72697 var showsPano = context.photos().showsPanoramic();
72698 var showsFlat = context.photos().showsFlat();
72699 var fromDate = context.photos().fromDate();
72700 var toDate = context.photos().toDate();
72701 var usernames = context.photos().usernames();
72703 if (!showsPano || !showsFlat) {
72704 images = images.filter(function (image) {
72705 if (image.pano) return showsPano;
72711 var fromTimestamp = new Date(fromDate).getTime();
72712 images = images.filter(function (image) {
72713 return new Date(image.captured_at).getTime() >= fromTimestamp;
72718 var toTimestamp = new Date(toDate).getTime();
72719 images = images.filter(function (image) {
72720 return new Date(image.captured_at).getTime() <= toTimestamp;
72725 images = images.filter(function (image) {
72726 return usernames.indexOf(image.captured_by) !== -1;
72733 function filterSequences(sequences, service) {
72734 var showsPano = context.photos().showsPanoramic();
72735 var showsFlat = context.photos().showsFlat();
72736 var fromDate = context.photos().fromDate();
72737 var toDate = context.photos().toDate();
72738 var usernames = context.photos().usernames();
72740 if (!showsPano || !showsFlat) {
72741 sequences = sequences.filter(function (sequence) {
72742 if (sequence.properties.hasOwnProperty('pano')) {
72743 if (sequence.properties.pano) return showsPano;
72746 // if the sequence doesn't specify pano or not, search its images
72747 var cProps = sequence.properties.coordinateProperties;
72749 if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
72750 for (var index in cProps.image_keys) {
72751 var imageKey = cProps.image_keys[index];
72752 var image = service.cachedImage(imageKey);
72754 if (image && image.hasOwnProperty('pano')) {
72755 if (image.pano) return showsPano;
72767 var fromTimestamp = new Date(fromDate).getTime();
72768 sequences = sequences.filter(function (sequence) {
72769 return new Date(sequence.properties.captured_at).getTime() >= fromTimestamp;
72774 var toTimestamp = new Date(toDate).getTime();
72775 sequences = sequences.filter(function (sequence) {
72776 return new Date(sequence.properties.captured_at).getTime() <= toTimestamp;
72781 sequences = sequences.filter(function (sequence) {
72782 return usernames.indexOf(sequence.properties.username) !== -1;
72789 function update() {
72790 var z = ~~context.map().zoom();
72791 var showMarkers = z >= minMarkerZoom;
72792 var showViewfields = z >= minViewfieldZoom;
72793 var service = getService();
72794 var sequences = service ? service.sequences(projection) : [];
72795 var images = service && showMarkers ? service.images(projection) : [];
72796 images = filterImages(images);
72797 sequences = filterSequences(sequences, service);
72798 service.filterViewer(context);
72799 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
72800 return d.properties.key;
72803 traces.exit().remove(); // enter/update
72805 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
72806 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
72810 groups.exit().remove(); // enter
72812 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
72813 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72815 var markers = groups.merge(groupsEnter).sort(function (a, b) {
72816 return b.loc[1] - a.loc[1]; // sort Y
72817 }).attr('transform', transform).select('.viewfield-scale');
72818 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72819 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72820 viewfields.exit().remove();
72821 viewfields.enter() // viewfields may or may not be drawn...
72822 .insert('path', 'circle') // but if they are, draw below the circles
72823 .attr('class', 'viewfield').classed('pano', function () {
72824 return this.parentNode.__data__.pano;
72825 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
72827 function viewfieldPath() {
72828 var d = this.parentNode.__data__;
72831 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
72833 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
72838 function drawImages(selection) {
72839 var enabled = svgMapillaryImages.enabled;
72840 var service = getService();
72841 layer = selection.selectAll('.layer-mapillary').data(service ? [0] : []);
72842 layer.exit().remove();
72843 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary').style('display', enabled ? 'block' : 'none');
72844 layerEnter.append('g').attr('class', 'sequences');
72845 layerEnter.append('g').attr('class', 'markers');
72846 layer = layerEnter.merge(layer);
72849 if (service && ~~context.map().zoom() >= minZoom) {
72852 service.loadImages(projection);
72859 drawImages.enabled = function (_) {
72860 if (!arguments.length) return svgMapillaryImages.enabled;
72861 svgMapillaryImages.enabled = _;
72863 if (svgMapillaryImages.enabled) {
72869 dispatch.call('change');
72873 drawImages.supported = function () {
72874 return !!getService();
72881 function svgMapillaryPosition(projection, context) {
72882 var throttledRedraw = throttle(function () {
72887 var minViewfieldZoom = 18;
72888 var layer = select(null);
72892 var viewerCompassAngle;
72895 if (svgMapillaryPosition.initialized) return; // run once
72897 svgMapillaryPosition.initialized = true;
72900 function getService() {
72901 if (services.mapillary && !_mapillary) {
72902 _mapillary = services.mapillary;
72904 _mapillary.event.on('nodeChanged', throttledRedraw);
72906 _mapillary.event.on('bearingChanged', function (e) {
72907 viewerCompassAngle = e;
72908 if (context.map().isTransformed()) return;
72909 layer.selectAll('.viewfield-group.currentView').filter(function (d) {
72911 }).attr('transform', transform);
72913 } else if (!services.mapillary && _mapillary) {
72920 function editOn() {
72921 layer.style('display', 'block');
72924 function editOff() {
72925 layer.selectAll('.viewfield-group').remove();
72926 layer.style('display', 'none');
72929 function transform(d) {
72930 var t = svgPointTransform(projection)(d);
72932 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
72933 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
72935 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
72941 function update() {
72942 var z = ~~context.map().zoom();
72943 var showViewfields = z >= minViewfieldZoom;
72944 var service = getService();
72945 var node = service && service.getActiveImage();
72946 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(node ? [node] : [], function (d) {
72950 groups.exit().remove(); // enter
72952 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group currentView highlighted');
72953 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72955 var markers = groups.merge(groupsEnter).attr('transform', transform).select('.viewfield-scale');
72956 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72957 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72958 viewfields.exit().remove();
72959 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').classed('pano', function () {
72960 return this.parentNode.__data__.pano;
72961 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
72963 function viewfieldPath() {
72964 var d = this.parentNode.__data__;
72967 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
72969 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
72974 function drawImages(selection) {
72975 var service = getService();
72976 layer = selection.selectAll('.layer-mapillary-position').data(service ? [0] : []);
72977 layer.exit().remove();
72978 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary-position');
72979 layerEnter.append('g').attr('class', 'markers');
72980 layer = layerEnter.merge(layer);
72982 if (service && ~~context.map().zoom() >= minZoom) {
72990 drawImages.enabled = function () {
72995 drawImages.supported = function () {
72996 return !!getService();
73003 function svgMapillarySigns(projection, context, dispatch) {
73004 var throttledRedraw = throttle(function () {
73005 dispatch.call('change');
73009 var layer = select(null);
73014 if (svgMapillarySigns.initialized) return; // run once
73016 svgMapillarySigns.enabled = false;
73017 svgMapillarySigns.initialized = true;
73020 function getService() {
73021 if (services.mapillary && !_mapillary) {
73022 _mapillary = services.mapillary;
73024 _mapillary.event.on('loadedSigns', throttledRedraw);
73025 } else if (!services.mapillary && _mapillary) {
73032 function showLayer() {
73033 var service = getService();
73034 if (!service) return;
73035 service.loadSignResources(context);
73039 function hideLayer() {
73040 throttledRedraw.cancel();
73044 function editOn() {
73045 layer.style('display', 'block');
73048 function editOff() {
73049 layer.selectAll('.icon-sign').remove();
73050 layer.style('display', 'none');
73053 function click(d3_event, d) {
73054 var service = getService();
73055 if (!service) return;
73056 context.map().centerEase(d.loc);
73057 var selectedImageKey = service.getSelectedImageKey();
73059 var highlightedDetection; // Pick one of the images the sign was detected in,
73060 // preference given to an image already selected.
73062 d.detections.forEach(function (detection) {
73063 if (!imageKey || selectedImageKey === detection.image_key) {
73064 imageKey = detection.image_key;
73065 highlightedDetection = detection;
73069 if (imageKey === selectedImageKey) {
73070 service.highlightDetection(highlightedDetection).selectImage(context, imageKey);
73072 service.ensureViewerLoaded(context).then(function () {
73073 service.highlightDetection(highlightedDetection).selectImage(context, imageKey).showViewer(context);
73078 function update() {
73079 var service = getService();
73080 var data = service ? service.signs(projection) : [];
73081 var selectedImageKey = service.getSelectedImageKey();
73082 var transform = svgPointTransform(projection);
73083 var signs = layer.selectAll('.icon-sign').data(data, function (d) {
73087 signs.exit().remove(); // enter
73089 var enter = signs.enter().append('g').attr('class', 'icon-sign icon-detected').on('click', click);
73090 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
73091 return '#' + d.value;
73093 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
73095 signs.merge(enter).attr('transform', transform).classed('currentView', function (d) {
73096 return d.detections.some(function (detection) {
73097 return detection.image_key === selectedImageKey;
73099 }).sort(function (a, b) {
73100 var aSelected = a.detections.some(function (detection) {
73101 return detection.image_key === selectedImageKey;
73103 var bSelected = b.detections.some(function (detection) {
73104 return detection.image_key === selectedImageKey;
73107 if (aSelected === bSelected) {
73108 return b.loc[1] - a.loc[1]; // sort Y
73109 } else if (aSelected) {
73117 function drawSigns(selection) {
73118 var enabled = svgMapillarySigns.enabled;
73119 var service = getService();
73120 layer = selection.selectAll('.layer-mapillary-signs').data(service ? [0] : []);
73121 layer.exit().remove();
73122 layer = layer.enter().append('g').attr('class', 'layer-mapillary-signs layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
73125 if (service && ~~context.map().zoom() >= minZoom) {
73128 service.loadSigns(projection);
73129 service.showSignDetections(true);
73133 } else if (service) {
73134 service.showSignDetections(false);
73138 drawSigns.enabled = function (_) {
73139 if (!arguments.length) return svgMapillarySigns.enabled;
73140 svgMapillarySigns.enabled = _;
73142 if (svgMapillarySigns.enabled) {
73148 dispatch.call('change');
73152 drawSigns.supported = function () {
73153 return !!getService();
73160 function svgMapillaryMapFeatures(projection, context, dispatch) {
73161 var throttledRedraw = throttle(function () {
73162 dispatch.call('change');
73166 var layer = select(null);
73171 if (svgMapillaryMapFeatures.initialized) return; // run once
73173 svgMapillaryMapFeatures.enabled = false;
73174 svgMapillaryMapFeatures.initialized = true;
73177 function getService() {
73178 if (services.mapillary && !_mapillary) {
73179 _mapillary = services.mapillary;
73181 _mapillary.event.on('loadedMapFeatures', throttledRedraw);
73182 } else if (!services.mapillary && _mapillary) {
73189 function showLayer() {
73190 var service = getService();
73191 if (!service) return;
73192 service.loadObjectResources(context);
73196 function hideLayer() {
73197 throttledRedraw.cancel();
73201 function editOn() {
73202 layer.style('display', 'block');
73205 function editOff() {
73206 layer.selectAll('.icon-map-feature').remove();
73207 layer.style('display', 'none');
73210 function click(d3_event, d) {
73211 var service = getService();
73212 if (!service) return;
73213 context.map().centerEase(d.loc);
73214 var selectedImageKey = service.getSelectedImageKey();
73216 var highlightedDetection; // Pick one of the images the map feature was detected in,
73217 // preference given to an image already selected.
73219 d.detections.forEach(function (detection) {
73220 if (!imageKey || selectedImageKey === detection.image_key) {
73221 imageKey = detection.image_key;
73222 highlightedDetection = detection;
73226 if (imageKey === selectedImageKey) {
73227 service.highlightDetection(highlightedDetection).selectImage(context, imageKey);
73229 service.ensureViewerLoaded(context).then(function () {
73230 service.highlightDetection(highlightedDetection).selectImage(context, imageKey).showViewer(context);
73235 function update() {
73236 var service = getService();
73237 var data = service ? service.mapFeatures(projection) : [];
73238 var selectedImageKey = service && service.getSelectedImageKey();
73239 var transform = svgPointTransform(projection);
73240 var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
73244 mapFeatures.exit().remove(); // enter
73246 var enter = mapFeatures.enter().append('g').attr('class', 'icon-map-feature icon-detected').on('click', click);
73247 enter.append('title').text(function (d) {
73248 var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
73249 return _t('mapillary_map_features.' + id);
73251 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
73252 if (d.value === 'object--billboard') {
73253 // no billboard icon right now, so use the advertisement icon
73254 return '#object--sign--advertisement';
73257 return '#' + d.value;
73259 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
73261 mapFeatures.merge(enter).attr('transform', transform).classed('currentView', function (d) {
73262 return d.detections.some(function (detection) {
73263 return detection.image_key === selectedImageKey;
73265 }).sort(function (a, b) {
73266 var aSelected = a.detections.some(function (detection) {
73267 return detection.image_key === selectedImageKey;
73269 var bSelected = b.detections.some(function (detection) {
73270 return detection.image_key === selectedImageKey;
73273 if (aSelected === bSelected) {
73274 return b.loc[1] - a.loc[1]; // sort Y
73275 } else if (aSelected) {
73283 function drawMapFeatures(selection) {
73284 var enabled = svgMapillaryMapFeatures.enabled;
73285 var service = getService();
73286 layer = selection.selectAll('.layer-mapillary-map-features').data(service ? [0] : []);
73287 layer.exit().remove();
73288 layer = layer.enter().append('g').attr('class', 'layer-mapillary-map-features layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
73291 if (service && ~~context.map().zoom() >= minZoom) {
73294 service.loadMapFeatures(projection);
73295 service.showFeatureDetections(true);
73299 } else if (service) {
73300 service.showFeatureDetections(false);
73304 drawMapFeatures.enabled = function (_) {
73305 if (!arguments.length) return svgMapillaryMapFeatures.enabled;
73306 svgMapillaryMapFeatures.enabled = _;
73308 if (svgMapillaryMapFeatures.enabled) {
73314 dispatch.call('change');
73318 drawMapFeatures.supported = function () {
73319 return !!getService();
73323 return drawMapFeatures;
73326 function svgOpenstreetcamImages(projection, context, dispatch) {
73327 var throttledRedraw = throttle(function () {
73328 dispatch.call('change');
73332 var minMarkerZoom = 16;
73333 var minViewfieldZoom = 18;
73334 var layer = select(null);
73336 var _openstreetcam;
73339 if (svgOpenstreetcamImages.initialized) return; // run once
73341 svgOpenstreetcamImages.enabled = false;
73342 svgOpenstreetcamImages.initialized = true;
73345 function getService() {
73346 if (services.openstreetcam && !_openstreetcam) {
73347 _openstreetcam = services.openstreetcam;
73349 _openstreetcam.event.on('loadedImages', throttledRedraw);
73350 } else if (!services.openstreetcam && _openstreetcam) {
73351 _openstreetcam = null;
73354 return _openstreetcam;
73357 function showLayer() {
73358 var service = getService();
73359 if (!service) return;
73361 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
73362 dispatch.call('change');
73366 function hideLayer() {
73367 throttledRedraw.cancel();
73368 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
73371 function editOn() {
73372 layer.style('display', 'block');
73375 function editOff() {
73376 layer.selectAll('.viewfield-group').remove();
73377 layer.style('display', 'none');
73380 function click(d3_event, d) {
73381 var service = getService();
73382 if (!service) return;
73383 service.ensureViewerLoaded(context).then(function () {
73384 service.selectImage(context, d.key).showViewer(context);
73386 context.map().centerEase(d.loc);
73389 function mouseover(d3_event, d) {
73390 var service = getService();
73391 if (service) service.setStyles(context, d);
73394 function mouseout() {
73395 var service = getService();
73396 if (service) service.setStyles(context, null);
73399 function transform(d) {
73400 var t = svgPointTransform(projection)(d);
73403 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
73409 context.photos().on('change.openstreetcam_images', update);
73411 function filterImages(images) {
73412 var fromDate = context.photos().fromDate();
73413 var toDate = context.photos().toDate();
73414 var usernames = context.photos().usernames();
73417 var fromTimestamp = new Date(fromDate).getTime();
73418 images = images.filter(function (item) {
73419 return new Date(item.captured_at).getTime() >= fromTimestamp;
73424 var toTimestamp = new Date(toDate).getTime();
73425 images = images.filter(function (item) {
73426 return new Date(item.captured_at).getTime() <= toTimestamp;
73431 images = images.filter(function (item) {
73432 return usernames.indexOf(item.captured_by) !== -1;
73439 function filterSequences(sequences) {
73440 var fromDate = context.photos().fromDate();
73441 var toDate = context.photos().toDate();
73442 var usernames = context.photos().usernames();
73445 var fromTimestamp = new Date(fromDate).getTime();
73446 sequences = sequences.filter(function (image) {
73447 return new Date(image.properties.captured_at).getTime() >= fromTimestamp;
73452 var toTimestamp = new Date(toDate).getTime();
73453 sequences = sequences.filter(function (image) {
73454 return new Date(image.properties.captured_at).getTime() <= toTimestamp;
73459 sequences = sequences.filter(function (image) {
73460 return usernames.indexOf(image.properties.captured_by) !== -1;
73467 function update() {
73468 var viewer = context.container().select('.photoviewer');
73469 var selected = viewer.empty() ? undefined : viewer.datum();
73470 var z = ~~context.map().zoom();
73471 var showMarkers = z >= minMarkerZoom;
73472 var showViewfields = z >= minViewfieldZoom;
73473 var service = getService();
73474 var sequences = [];
73477 if (context.photos().showsFlat()) {
73478 sequences = service ? service.sequences(projection) : [];
73479 images = service && showMarkers ? service.images(projection) : [];
73480 sequences = filterSequences(sequences);
73481 images = filterImages(images);
73484 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
73485 return d.properties.key;
73488 traces.exit().remove(); // enter/update
73490 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
73491 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
73495 groups.exit().remove(); // enter
73497 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
73498 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
73500 var markers = groups.merge(groupsEnter).sort(function (a, b) {
73501 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; // sort Y
73502 }).attr('transform', transform).select('.viewfield-scale');
73503 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
73504 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
73505 viewfields.exit().remove();
73506 viewfields.enter() // viewfields may or may not be drawn...
73507 .insert('path', 'circle') // but if they are, draw below the circles
73508 .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');
73511 function drawImages(selection) {
73512 var enabled = svgOpenstreetcamImages.enabled,
73513 service = getService();
73514 layer = selection.selectAll('.layer-openstreetcam').data(service ? [0] : []);
73515 layer.exit().remove();
73516 var layerEnter = layer.enter().append('g').attr('class', 'layer-openstreetcam').style('display', enabled ? 'block' : 'none');
73517 layerEnter.append('g').attr('class', 'sequences');
73518 layerEnter.append('g').attr('class', 'markers');
73519 layer = layerEnter.merge(layer);
73522 if (service && ~~context.map().zoom() >= minZoom) {
73525 service.loadImages(projection);
73532 drawImages.enabled = function (_) {
73533 if (!arguments.length) return svgOpenstreetcamImages.enabled;
73534 svgOpenstreetcamImages.enabled = _;
73536 if (svgOpenstreetcamImages.enabled) {
73542 dispatch.call('change');
73546 drawImages.supported = function () {
73547 return !!getService();
73554 function svgOsm(projection, context, dispatch) {
73555 var enabled = true;
73557 function drawOsm(selection) {
73558 selection.selectAll('.layer-osm').data(['covered', 'areas', 'lines', 'points', 'labels']).enter().append('g').attr('class', function (d) {
73559 return 'layer-osm ' + d;
73561 selection.selectAll('.layer-osm.points').selectAll('.points-group').data(['points', 'midpoints', 'vertices', 'turns']).enter().append('g').attr('class', function (d) {
73562 return 'points-group ' + d;
73566 function showLayer() {
73567 var layer = context.surface().selectAll('.data-layer.osm');
73569 layer.classed('disabled', false).style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
73570 dispatch.call('change');
73574 function hideLayer() {
73575 var layer = context.surface().selectAll('.data-layer.osm');
73577 layer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
73578 layer.classed('disabled', true);
73579 dispatch.call('change');
73583 drawOsm.enabled = function (val) {
73584 if (!arguments.length) return enabled;
73593 dispatch.call('change');
73600 var _notesEnabled = false;
73604 function svgNotes(projection, context, dispatch$1) {
73606 dispatch$1 = dispatch('change');
73609 var throttledRedraw = throttle(function () {
73610 dispatch$1.call('change');
73614 var touchLayer = select(null);
73615 var drawLayer = select(null);
73616 var _notesVisible = false;
73618 function markerPath(selection, klass) {
73619 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');
73620 } // Loosely-coupled osm service for fetching notes.
73623 function getService() {
73624 if (services.osm && !_osmService) {
73625 _osmService = services.osm;
73627 _osmService.on('loadedNotes', throttledRedraw);
73628 } else if (!services.osm && _osmService) {
73629 _osmService = null;
73632 return _osmService;
73633 } // Show the notes
73636 function editOn() {
73637 if (!_notesVisible) {
73638 _notesVisible = true;
73639 drawLayer.style('display', 'block');
73641 } // Immediately remove the notes and their touch targets
73644 function editOff() {
73645 if (_notesVisible) {
73646 _notesVisible = false;
73647 drawLayer.style('display', 'none');
73648 drawLayer.selectAll('.note').remove();
73649 touchLayer.selectAll('.note').remove();
73651 } // Enable the layer. This shows the notes and transitions them to visible.
73654 function layerOn() {
73656 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
73657 dispatch$1.call('change');
73659 } // Disable the layer. This transitions the layer invisible and then hides the notes.
73662 function layerOff() {
73663 throttledRedraw.cancel();
73664 drawLayer.interrupt();
73665 touchLayer.selectAll('.note').remove();
73666 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
73668 dispatch$1.call('change');
73670 } // Update the note markers
73673 function updateMarkers() {
73674 if (!_notesVisible || !_notesEnabled) return;
73675 var service = getService();
73676 var selectedID = context.selectedNoteID();
73677 var data = service ? service.notes(projection) : [];
73678 var getTransform = svgPointTransform(projection); // Draw markers..
73680 var notes = drawLayer.selectAll('.note').data(data, function (d) {
73681 return d.status + d.id;
73684 notes.exit().remove(); // enter
73686 var notesEnter = notes.enter().append('g').attr('class', function (d) {
73687 return 'note note-' + d.id + ' ' + d.status;
73688 }).classed('new', function (d) {
73691 notesEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
73692 notesEnter.append('path').call(markerPath, 'shadow');
73693 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');
73694 notesEnter.selectAll('.icon-annotation').data(function (d) {
73696 }).enter().append('use').attr('class', 'icon-annotation').attr('width', '10px').attr('height', '10px').attr('x', '-3px').attr('y', '-19px').attr('xlink:href', function (d) {
73697 return '#iD-icon-' + (d.id < 0 ? 'plus' : d.status === 'open' ? 'close' : 'apply');
73700 notes.merge(notesEnter).sort(sortY).classed('selected', function (d) {
73701 var mode = context.mode();
73702 var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging
73704 return !isMoving && d.id === selectedID;
73705 }).attr('transform', getTransform); // Draw targets..
73707 if (touchLayer.empty()) return;
73708 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73709 var targets = touchLayer.selectAll('.note').data(data, function (d) {
73713 targets.exit().remove(); // enter/update
73715 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
73716 var newClass = d.id < 0 ? 'new' : '';
73717 return 'note target note-' + d.id + ' ' + fillClass + newClass;
73718 }).attr('transform', getTransform);
73720 function sortY(a, b) {
73721 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
73723 } // Draw the notes layer and schedule loading notes and updating markers.
73726 function drawNotes(selection) {
73727 var service = getService();
73728 var surface = context.surface();
73730 if (surface && !surface.empty()) {
73731 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
73734 drawLayer = selection.selectAll('.layer-notes').data(service ? [0] : []);
73735 drawLayer.exit().remove();
73736 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-notes').style('display', _notesEnabled ? 'block' : 'none').merge(drawLayer);
73738 if (_notesEnabled) {
73739 if (service && ~~context.map().zoom() >= minZoom) {
73741 service.loadNotes(projection);
73747 } // Toggles the layer on and off
73750 drawNotes.enabled = function (val) {
73751 if (!arguments.length) return _notesEnabled;
73752 _notesEnabled = val;
73754 if (_notesEnabled) {
73759 if (context.selectedNoteID()) {
73760 context.enter(modeBrowse(context));
73764 dispatch$1.call('change');
73771 function svgTouch() {
73772 function drawTouch(selection) {
73773 selection.selectAll('.layer-touch').data(['areas', 'lines', 'points', 'turns', 'markers']).enter().append('g').attr('class', function (d) {
73774 return 'layer-touch ' + d;
73781 function refresh(selection, node) {
73782 var cr = node.getBoundingClientRect();
73783 var prop = [cr.width, cr.height];
73784 selection.property('__dimensions__', prop);
73788 function utilGetDimensions(selection, force) {
73789 if (!selection || selection.empty()) {
73793 var node = selection.node(),
73794 cached = selection.property('__dimensions__');
73795 return !cached || force ? refresh(selection, node) : cached;
73797 function utilSetDimensions(selection, dimensions) {
73798 if (!selection || selection.empty()) {
73802 var node = selection.node();
73804 if (dimensions === null) {
73805 refresh(selection, node);
73809 return selection.property('__dimensions__', [dimensions[0], dimensions[1]]).attr('width', dimensions[0]).attr('height', dimensions[1]);
73812 function svgLayers(projection, context) {
73813 var dispatch$1 = dispatch('change');
73814 var svg = select(null);
73817 layer: svgOsm(projection, context, dispatch$1)
73820 layer: svgNotes(projection, context, dispatch$1)
73823 layer: svgData(projection, context, dispatch$1)
73826 layer: svgKeepRight(projection, context, dispatch$1)
73829 layer: svgImproveOSM(projection, context, dispatch$1)
73832 layer: svgOsmose(projection, context, dispatch$1)
73835 layer: svgStreetside(projection, context, dispatch$1)
73838 layer: svgMapillaryImages(projection, context, dispatch$1)
73840 id: 'mapillary-position',
73841 layer: svgMapillaryPosition(projection, context)
73843 id: 'mapillary-map-features',
73844 layer: svgMapillaryMapFeatures(projection, context, dispatch$1)
73846 id: 'mapillary-signs',
73847 layer: svgMapillarySigns(projection, context, dispatch$1)
73849 id: 'openstreetcam',
73850 layer: svgOpenstreetcamImages(projection, context, dispatch$1)
73853 layer: svgDebug(projection, context)
73856 layer: svgGeolocate(projection)
73862 function drawLayers(selection) {
73863 svg = selection.selectAll('.surface').data([0]);
73864 svg = svg.enter().append('svg').attr('class', 'surface').merge(svg);
73865 var defs = svg.selectAll('.surface-defs').data([0]);
73866 defs.enter().append('defs').attr('class', 'surface-defs');
73867 var groups = svg.selectAll('.data-layer').data(_layers);
73868 groups.exit().remove();
73869 groups.enter().append('g').attr('class', function (d) {
73870 return 'data-layer ' + d.id;
73871 }).merge(groups).each(function (d) {
73872 select(this).call(d.layer);
73876 drawLayers.all = function () {
73880 drawLayers.layer = function (id) {
73881 var obj = _layers.find(function (o) {
73882 return o.id === id;
73885 return obj && obj.layer;
73888 drawLayers.only = function (what) {
73889 var arr = [].concat(what);
73891 var all = _layers.map(function (layer) {
73895 return drawLayers.remove(utilArrayDifference(all, arr));
73898 drawLayers.remove = function (what) {
73899 var arr = [].concat(what);
73900 arr.forEach(function (id) {
73901 _layers = _layers.filter(function (o) {
73902 return o.id !== id;
73905 dispatch$1.call('change');
73909 drawLayers.add = function (what) {
73910 var arr = [].concat(what);
73911 arr.forEach(function (obj) {
73912 if ('id' in obj && 'layer' in obj) {
73916 dispatch$1.call('change');
73920 drawLayers.dimensions = function (val) {
73921 if (!arguments.length) return utilGetDimensions(svg);
73922 utilSetDimensions(svg, val);
73926 return utilRebind(drawLayers, dispatch$1, 'on');
73929 function svgLines(projection, context) {
73930 var detected = utilDetect();
73931 var highway_stack = {
73946 function drawTargets(selection, graph, entities, filter) {
73947 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73948 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
73949 var getPath = svgPath(projection).geojson;
73950 var activeID = context.activeID();
73951 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
73957 entities.forEach(function (way) {
73958 var features = svgSegmentWay(way, graph, activeID);
73959 data.targets.push.apply(data.targets, features.passive);
73960 data.nopes.push.apply(data.nopes, features.active);
73961 }); // Targets allow hover and vertex snapping
73963 var targetData = data.targets.filter(getPath);
73964 var targets = selection.selectAll('.line.target-allowed').filter(function (d) {
73965 return filter(d.properties.entity);
73966 }).data(targetData, function key(d) {
73970 targets.exit().remove();
73972 var segmentWasEdited = function segmentWasEdited(d) {
73973 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
73975 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
73979 return d.properties.nodes.some(function (n) {
73980 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
73985 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
73986 return 'way line target target-allowed ' + targetClass + d.id;
73987 }).classed('segment-edited', segmentWasEdited); // NOPE
73989 var nopeData = data.nopes.filter(getPath);
73990 var nopes = selection.selectAll('.line.target-nope').filter(function (d) {
73991 return filter(d.properties.entity);
73992 }).data(nopeData, function key(d) {
73996 nopes.exit().remove(); // enter/update
73998 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
73999 return 'way line target target-nope ' + nopeClass + d.id;
74000 }).classed('segment-edited', segmentWasEdited);
74003 function drawLines(selection, graph, entities, filter) {
74004 var base = context.history().base();
74006 function waystack(a, b) {
74007 var selected = context.selectedIDs();
74008 var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
74009 var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
74011 if (a.tags.highway) {
74012 scoreA -= highway_stack[a.tags.highway];
74015 if (b.tags.highway) {
74016 scoreB -= highway_stack[b.tags.highway];
74019 return scoreA - scoreB;
74022 function drawLineGroup(selection, klass, isSelected) {
74023 // Note: Don't add `.selected` class in draw modes
74024 var mode = context.mode();
74025 var isDrawing = mode && /^draw/.test(mode.id);
74026 var selectedClass = !isDrawing && isSelected ? 'selected ' : '';
74027 var lines = selection.selectAll('path').filter(filter).data(getPathData(isSelected), osmEntity.key);
74028 lines.exit().remove(); // Optimization: Call expensive TagClasses only on enter selection. This
74029 // works because osmEntity.key is defined to include the entity v attribute.
74031 lines.enter().append('path').attr('class', function (d) {
74032 var prefix = 'way line'; // if this line isn't styled by its own tags
74034 if (!d.hasInterestingTags()) {
74035 var parentRelations = graph.parentRelations(d);
74036 var parentMultipolygons = parentRelations.filter(function (relation) {
74037 return relation.isMultipolygon();
74038 }); // and if it's a member of at least one multipolygon relation
74040 if (parentMultipolygons.length > 0 && // and only multipolygon relations
74041 parentRelations.length === parentMultipolygons.length) {
74042 // then fudge the classes to style this as an area edge
74043 prefix = 'relation area';
74047 var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
74048 return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
74049 }).classed('added', function (d) {
74050 return !base.entities[d.id];
74051 }).classed('geometry-edited', function (d) {
74052 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
74053 }).classed('retagged', function (d) {
74054 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74055 }).call(svgTagClasses()).merge(lines).sort(waystack).attr('d', getPath).call(svgTagClasses().tags(svgRelationMemberTags(graph)));
74059 function getPathData(isSelected) {
74060 return function () {
74061 var layer = this.parentNode.__data__;
74062 var data = pathdata[layer] || [];
74063 return data.filter(function (d) {
74064 if (isSelected) return context.selectedIDs().indexOf(d.id) !== -1;else return context.selectedIDs().indexOf(d.id) === -1;
74069 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
74070 var markergroup = layergroup.selectAll('g.' + groupclass).data([pathclass]);
74071 markergroup = markergroup.enter().append('g').attr('class', groupclass).merge(markergroup);
74072 var markers = markergroup.selectAll('path').filter(filter).data(function data() {
74073 return groupdata[this.parentNode.__data__] || [];
74074 }, function key(d) {
74075 return [d.id, d.index];
74077 markers.exit().remove();
74078 markers = markers.enter().append('path').attr('class', pathclass).merge(markers).attr('marker-mid', marker).attr('d', function (d) {
74083 markers.each(function () {
74084 this.parentNode.insertBefore(this, this);
74089 var getPath = svgPath(projection, graph);
74091 var onewaydata = {};
74092 var sideddata = {};
74093 var oldMultiPolygonOuters = {};
74095 for (var i = 0; i < entities.length; i++) {
74096 var entity = entities[i];
74097 var outer = osmOldMultipolygonOuterMember(entity, graph);
74100 ways.push(entity.mergeTags(outer.tags));
74101 oldMultiPolygonOuters[outer.id] = true;
74102 } else if (entity.geometry(graph) === 'line') {
74107 ways = ways.filter(getPath);
74108 var pathdata = utilArrayGroupBy(ways, function (way) {
74109 return way.layer();
74111 Object.keys(pathdata).forEach(function (k) {
74112 var v = pathdata[k];
74113 var onewayArr = v.filter(function (d) {
74114 return d.isOneWay();
74116 var onewaySegments = svgMarkerSegments(projection, graph, 35, function shouldReverse(entity) {
74117 return entity.tags.oneway === '-1';
74118 }, function bothDirections(entity) {
74119 return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
74121 onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
74122 var sidedArr = v.filter(function (d) {
74123 return d.isSided();
74125 var sidedSegments = svgMarkerSegments(projection, graph, 30, function shouldReverse() {
74127 }, function bothDirections() {
74130 sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
74132 var covered = selection.selectAll('.layer-osm.covered'); // under areas
74134 var uncovered = selection.selectAll('.layer-osm.lines'); // over areas
74136 var touchLayer = selection.selectAll('.layer-touch.lines'); // Draw lines..
74138 [covered, uncovered].forEach(function (selection) {
74139 var range$1 = selection === covered ? range(-10, 0) : range(0, 11);
74140 var layergroup = selection.selectAll('g.layergroup').data(range$1);
74141 layergroup = layergroup.enter().append('g').attr('class', function (d) {
74142 return 'layergroup layer' + String(d);
74143 }).merge(layergroup);
74144 layergroup.selectAll('g.linegroup').data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']).enter().append('g').attr('class', function (d) {
74145 return 'linegroup line-' + d;
74147 layergroup.selectAll('g.line-shadow').call(drawLineGroup, 'shadow', false);
74148 layergroup.selectAll('g.line-casing').call(drawLineGroup, 'casing', false);
74149 layergroup.selectAll('g.line-stroke').call(drawLineGroup, 'stroke', false);
74150 layergroup.selectAll('g.line-shadow-highlighted').call(drawLineGroup, 'shadow', true);
74151 layergroup.selectAll('g.line-casing-highlighted').call(drawLineGroup, 'casing', true);
74152 layergroup.selectAll('g.line-stroke-highlighted').call(drawLineGroup, 'stroke', true);
74153 addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
74154 addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, function marker(d) {
74155 var category = graph.entity(d.id).sidednessIdentifier();
74156 return 'url(#ideditor-sided-marker-' + category + ')';
74158 }); // Draw touch targets..
74160 touchLayer.call(drawTargets, graph, ways, filter);
74166 function svgMidpoints(projection, context) {
74167 var targetRadius = 8;
74169 function drawTargets(selection, graph, entities, filter) {
74170 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74171 var getTransform = svgPointTransform(projection).geojson;
74172 var data = entities.map(function (midpoint) {
74182 coordinates: midpoint.loc
74186 var targets = selection.selectAll('.midpoint.target').filter(function (d) {
74187 return filter(d.properties.entity);
74188 }).data(data, function key(d) {
74192 targets.exit().remove(); // enter/update
74194 targets.enter().append('circle').attr('r', targetRadius).merge(targets).attr('class', function (d) {
74195 return 'node midpoint target ' + fillClass + d.id;
74196 }).attr('transform', getTransform);
74199 function drawMidpoints(selection, graph, entities, filter, extent) {
74200 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
74201 var touchLayer = selection.selectAll('.layer-touch.points');
74202 var mode = context.mode();
74204 if (mode && mode.id !== 'select' || !context.map().withinEditableZoom()) {
74205 drawLayer.selectAll('.midpoint').remove();
74206 touchLayer.selectAll('.midpoint.target').remove();
74210 var poly = extent.polygon();
74211 var midpoints = {};
74213 for (var i = 0; i < entities.length; i++) {
74214 var entity = entities[i];
74215 if (entity.type !== 'way') continue;
74216 if (!filter(entity)) continue;
74217 if (context.selectedIDs().indexOf(entity.id) < 0) continue;
74218 var nodes = graph.childNodes(entity);
74220 for (var j = 0; j < nodes.length - 1; j++) {
74222 var b = nodes[j + 1];
74223 var id = [a.id, b.id].sort().join('-');
74225 if (midpoints[id]) {
74226 midpoints[id].parents.push(entity);
74227 } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
74228 var point = geoVecInterp(a.loc, b.loc, 0.5);
74231 if (extent.intersects(point)) {
74234 for (var k = 0; k < 4; k++) {
74235 point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
74237 if (point && geoVecLength(projection(a.loc), projection(point)) > 20 && geoVecLength(projection(b.loc), projection(point)) > 20) {
74249 edge: [a.id, b.id],
74257 function midpointFilter(d) {
74258 if (midpoints[d.id]) return true;
74260 for (var i = 0; i < d.parents.length; i++) {
74261 if (filter(d.parents[i])) {
74269 var groups = drawLayer.selectAll('.midpoint').filter(midpointFilter).data(Object.values(midpoints), function (d) {
74272 groups.exit().remove();
74273 var enter = groups.enter().insert('g', ':first-child').attr('class', 'midpoint');
74274 enter.append('polygon').attr('points', '-6,8 10,0 -6,-8').attr('class', 'shadow');
74275 enter.append('polygon').attr('points', '-3,4 5,0 -3,-4').attr('class', 'fill');
74276 groups = groups.merge(enter).attr('transform', function (d) {
74277 var translate = svgPointTransform(projection);
74278 var a = graph.entity(d.edge[0]);
74279 var b = graph.entity(d.edge[1]);
74280 var angle = geoAngle(a, b, projection) * (180 / Math.PI);
74281 return translate(d) + ' rotate(' + angle + ')';
74282 }).call(svgTagClasses().tags(function (d) {
74283 return d.parents[0].tags;
74284 })); // Propagate data bindings.
74286 groups.select('polygon.shadow');
74287 groups.select('polygon.fill'); // Draw touch targets..
74289 touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter);
74292 return drawMidpoints;
74295 function svgPoints(projection, context) {
74296 function markerPath(selection, klass) {
74297 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');
74300 function sortY(a, b) {
74301 return b.loc[1] - a.loc[1];
74302 } // Avoid exit/enter if we're just moving stuff around.
74303 // The node will get a new version but we only need to run the update selection.
74306 function fastEntityKey(d) {
74307 var mode = context.mode();
74308 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74309 return isMoving ? d.id : osmEntity.key(d);
74312 function drawTargets(selection, graph, entities, filter) {
74313 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74314 var getTransform = svgPointTransform(projection).geojson;
74315 var activeID = context.activeID();
74317 entities.forEach(function (node) {
74318 if (activeID === node.id) return; // draw no target on the activeID
74327 geometry: node.asGeoJSON()
74330 var targets = selection.selectAll('.point.target').filter(function (d) {
74331 return filter(d.properties.entity);
74332 }).data(data, function key(d) {
74336 targets.exit().remove(); // enter/update
74338 targets.enter().append('rect').attr('x', -10).attr('y', -26).attr('width', 20).attr('height', 30).merge(targets).attr('class', function (d) {
74339 return 'node point target ' + fillClass + d.id;
74340 }).attr('transform', getTransform);
74343 function drawPoints(selection, graph, entities, filter) {
74344 var wireframe = context.surface().classed('fill-wireframe');
74345 var zoom = geoScaleToZoom(projection.scale());
74346 var base = context.history().base(); // Points with a direction will render as vertices at higher zooms..
74348 function renderAsPoint(entity) {
74349 return entity.geometry(graph) === 'point' && !(zoom >= 18 && entity.directions(graph, projection).length);
74350 } // All points will render as vertices in wireframe mode too..
74353 var points = wireframe ? [] : entities.filter(renderAsPoint);
74354 points.sort(sortY);
74355 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
74356 var touchLayer = selection.selectAll('.layer-touch.points'); // Draw points..
74358 var groups = drawLayer.selectAll('g.point').filter(filter).data(points, fastEntityKey);
74359 groups.exit().remove();
74360 var enter = groups.enter().append('g').attr('class', function (d) {
74361 return 'node point ' + d.id;
74363 enter.append('path').call(markerPath, 'shadow');
74364 enter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
74365 enter.append('path').call(markerPath, 'stroke');
74366 enter.append('use').attr('transform', 'translate(-5, -19)').attr('class', 'icon').attr('width', '11px').attr('height', '11px');
74367 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('added', function (d) {
74368 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
74369 }).classed('moved', function (d) {
74370 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
74371 }).classed('retagged', function (d) {
74372 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74373 }).call(svgTagClasses());
74374 groups.select('.shadow'); // propagate bound data
74376 groups.select('.stroke'); // propagate bound data
74378 groups.select('.icon') // propagate bound data
74379 .attr('xlink:href', function (entity) {
74380 var preset = _mainPresetIndex.match(entity, graph);
74381 var picon = preset && preset.icon;
74386 var isMaki = /^maki-/.test(picon);
74387 return '#' + picon + (isMaki ? '-11' : '');
74389 }); // Draw touch targets..
74391 touchLayer.call(drawTargets, graph, points, filter);
74397 function svgTurns(projection, context) {
74398 function icon(turn) {
74399 var u = turn.u ? '-u' : '';
74400 if (turn.no) return '#iD-turn-no' + u;
74401 if (turn.only) return '#iD-turn-only' + u;
74402 return '#iD-turn-yes' + u;
74405 function drawTurns(selection, graph, turns) {
74406 function turnTransform(d) {
74408 var toWay = graph.entity(d.to.way);
74409 var toPoints = graph.childNodes(toWay).map(function (n) {
74411 }).map(projection);
74412 var toLength = geoPathLength(toPoints);
74413 var mid = toLength / 2; // midpoint of destination way
74415 var toNode = graph.entity(d.to.node);
74416 var toVertex = graph.entity(d.to.vertex);
74417 var a = geoAngle(toVertex, toNode, projection);
74418 var o = projection(toVertex.loc);
74419 var r = d.u ? 0 // u-turn: no radius
74420 : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius
74421 : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways
74423 return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + 'rotate(' + a * 180 / Math.PI + ')';
74426 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
74427 var touchLayer = selection.selectAll('.layer-touch.turns'); // Draw turns..
74429 var groups = drawLayer.selectAll('g.turn').data(turns, function (d) {
74433 groups.exit().remove(); // enter
74435 var groupsEnter = groups.enter().append('g').attr('class', function (d) {
74436 return 'turn ' + d.key;
74438 var turnsEnter = groupsEnter.filter(function (d) {
74441 turnsEnter.append('rect').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74442 turnsEnter.append('use').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74443 var uEnter = groupsEnter.filter(function (d) {
74446 uEnter.append('circle').attr('r', '16');
74447 uEnter.append('use').attr('transform', 'translate(-16, -16)').attr('width', '32').attr('height', '32'); // update
74449 groups = groups.merge(groupsEnter).attr('opacity', function (d) {
74450 return d.direct === false ? '0.7' : null;
74451 }).attr('transform', turnTransform);
74452 groups.select('use').attr('xlink:href', icon);
74453 groups.select('rect'); // propagate bound data
74455 groups.select('circle'); // propagate bound data
74456 // Draw touch targets..
74458 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74459 groups = touchLayer.selectAll('g.turn').data(turns, function (d) {
74463 groups.exit().remove(); // enter
74465 groupsEnter = groups.enter().append('g').attr('class', function (d) {
74466 return 'turn ' + d.key;
74468 turnsEnter = groupsEnter.filter(function (d) {
74471 turnsEnter.append('rect').attr('class', 'target ' + fillClass).attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74472 uEnter = groupsEnter.filter(function (d) {
74475 uEnter.append('circle').attr('class', 'target ' + fillClass).attr('r', '16'); // update
74477 groups = groups.merge(groupsEnter).attr('transform', turnTransform);
74478 groups.select('rect'); // propagate bound data
74480 groups.select('circle'); // propagate bound data
74488 function svgVertices(projection, context) {
74490 // z16-, z17, z18+, w/icon
74491 shadow: [6, 7.5, 7.5, 12],
74492 stroke: [2.5, 3.5, 3.5, 8],
74493 fill: [1, 1.5, 1.5, 1.5]
74496 var _currHoverTarget;
74498 var _currPersistent = {};
74499 var _currHover = {};
74500 var _prevHover = {};
74501 var _currSelected = {};
74502 var _prevSelected = {};
74505 function sortY(a, b) {
74506 return b.loc[1] - a.loc[1];
74507 } // Avoid exit/enter if we're just moving stuff around.
74508 // The node will get a new version but we only need to run the update selection.
74511 function fastEntityKey(d) {
74512 var mode = context.mode();
74513 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74514 return isMoving ? d.id : osmEntity.key(d);
74517 function draw(selection, graph, vertices, sets, filter) {
74524 var directions = {};
74525 var wireframe = context.surface().classed('fill-wireframe');
74526 var zoom = geoScaleToZoom(projection.scale());
74527 var z = zoom < 17 ? 0 : zoom < 18 ? 1 : 2;
74528 var activeID = context.activeID();
74529 var base = context.history().base();
74531 function getIcon(d) {
74532 // always check latest entity, as fastEntityKey avoids enter/exit now
74533 var entity = graph.entity(d.id);
74534 if (entity.id in icons) return icons[entity.id];
74535 icons[entity.id] = entity.hasInterestingTags() && _mainPresetIndex.match(entity, graph).icon;
74536 return icons[entity.id];
74537 } // memoize directions results, return false for empty arrays (for use in filter)
74540 function getDirections(entity) {
74541 if (entity.id in directions) return directions[entity.id];
74542 var angles = entity.directions(graph, projection);
74543 directions[entity.id] = angles.length ? angles : false;
74547 function updateAttributes(selection) {
74548 ['shadow', 'stroke', 'fill'].forEach(function (klass) {
74549 var rads = radiuses[klass];
74550 selection.selectAll('.' + klass).each(function (entity) {
74551 var i = z && getIcon(entity);
74552 var r = rads[i ? 3 : z]; // slightly increase the size of unconnected endpoints #3775
74554 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
74558 if (klass === 'shadow') {
74559 // remember this value, so we don't need to
74560 _radii[entity.id] = r; // recompute it when we draw the touch targets
74563 select(this).attr('r', r).attr('visibility', i && klass === 'fill' ? 'hidden' : null);
74568 vertices.sort(sortY);
74569 var groups = selection.selectAll('g.vertex').filter(filter).data(vertices, fastEntityKey); // exit
74571 groups.exit().remove(); // enter
74573 var enter = groups.enter().append('g').attr('class', function (d) {
74574 return 'node vertex ' + d.id;
74576 enter.append('circle').attr('class', 'shadow');
74577 enter.append('circle').attr('class', 'stroke'); // Vertices with tags get a fill.
74579 enter.filter(function (d) {
74580 return d.hasInterestingTags();
74581 }).append('circle').attr('class', 'fill'); // update
74583 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('sibling', function (d) {
74584 return d.id in sets.selected;
74585 }).classed('shared', function (d) {
74586 return graph.isShared(d);
74587 }).classed('endpoint', function (d) {
74588 return d.isEndpoint(graph);
74589 }).classed('added', function (d) {
74590 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
74591 }).classed('moved', function (d) {
74592 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
74593 }).classed('retagged', function (d) {
74594 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74595 }).call(updateAttributes); // Vertices with icons get a `use`.
74597 var iconUse = groups.selectAll('.icon').data(function data(d) {
74598 return zoom >= 17 && getIcon(d) ? [d] : [];
74599 }, fastEntityKey); // exit
74601 iconUse.exit().remove(); // enter
74603 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) {
74604 var picon = getIcon(d);
74605 var isMaki = /^maki-/.test(picon);
74606 return '#' + picon + (isMaki ? '-11' : '');
74607 }); // Vertices with directions get viewfields
74609 var dgroups = groups.selectAll('.viewfieldgroup').data(function data(d) {
74610 return zoom >= 18 && getDirections(d) ? [d] : [];
74611 }, fastEntityKey); // exit
74613 dgroups.exit().remove(); // enter/update
74615 dgroups = dgroups.enter().insert('g', '.shadow').attr('class', 'viewfieldgroup').merge(dgroups);
74616 var viewfields = dgroups.selectAll('.viewfield').data(getDirections, function key(d) {
74617 return osmEntity.key(d);
74620 viewfields.exit().remove(); // enter/update
74622 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) {
74623 return 'rotate(' + d + ')';
74627 function drawTargets(selection, graph, entities, filter) {
74628 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74629 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
74630 var getTransform = svgPointTransform(projection).geojson;
74631 var activeID = context.activeID();
74636 entities.forEach(function (node) {
74637 if (activeID === node.id) return; // draw no target on the activeID
74639 var vertexType = svgPassiveVertex(node, graph, activeID);
74641 if (vertexType !== 0) {
74642 // passive or adjacent - allow to connect
74643 data.targets.push({
74650 geometry: node.asGeoJSON()
74655 id: node.id + '-nope',
74661 geometry: node.asGeoJSON()
74664 }); // Targets allow hover and vertex snapping
74666 var targets = selection.selectAll('.vertex.target-allowed').filter(function (d) {
74667 return filter(d.properties.entity);
74668 }).data(data.targets, function key(d) {
74672 targets.exit().remove(); // enter/update
74674 targets.enter().append('circle').attr('r', function (d) {
74675 return _radii[d.id] || radiuses.shadow[3];
74676 }).merge(targets).attr('class', function (d) {
74677 return 'node vertex target target-allowed ' + targetClass + d.id;
74678 }).attr('transform', getTransform); // NOPE
74680 var nopes = selection.selectAll('.vertex.target-nope').filter(function (d) {
74681 return filter(d.properties.entity);
74682 }).data(data.nopes, function key(d) {
74686 nopes.exit().remove(); // enter/update
74688 nopes.enter().append('circle').attr('r', function (d) {
74689 return _radii[d.properties.entity.id] || radiuses.shadow[3];
74690 }).merge(nopes).attr('class', function (d) {
74691 return 'node vertex target target-nope ' + nopeClass + d.id;
74692 }).attr('transform', getTransform);
74693 } // Points can also render as vertices:
74694 // 1. in wireframe mode or
74695 // 2. at higher zooms if they have a direction
74698 function renderAsVertex(entity, graph, wireframe, zoom) {
74699 var geometry = entity.geometry(graph);
74700 return geometry === 'vertex' || geometry === 'point' && (wireframe || zoom >= 18 && entity.directions(graph, projection).length);
74703 function isEditedNode(node, base, head) {
74704 var baseNode = base.entities[node.id];
74705 var headNode = head.entities[node.id];
74706 return !headNode || !baseNode || !fastDeepEqual(headNode.tags, baseNode.tags) || !fastDeepEqual(headNode.loc, baseNode.loc);
74709 function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
74713 function addChildVertices(entity) {
74714 // avoid redundant work and infinite recursion of circular relations
74715 if (seenIds[entity.id]) return;
74716 seenIds[entity.id] = true;
74717 var geometry = entity.geometry(graph);
74719 if (!context.features().isHiddenFeature(entity, graph, geometry)) {
74722 if (entity.type === 'way') {
74723 for (i = 0; i < entity.nodes.length; i++) {
74724 var child = graph.hasEntity(entity.nodes[i]);
74727 addChildVertices(child);
74730 } else if (entity.type === 'relation') {
74731 for (i = 0; i < entity.members.length; i++) {
74732 var member = graph.hasEntity(entity.members[i].id);
74735 addChildVertices(member);
74738 } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
74739 results[entity.id] = entity;
74744 ids.forEach(function (id) {
74745 var entity = graph.hasEntity(id);
74746 if (!entity) return;
74748 if (entity.type === 'node') {
74749 if (renderAsVertex(entity, graph, wireframe, zoom)) {
74750 results[entity.id] = entity;
74751 graph.parentWays(entity).forEach(function (entity) {
74752 addChildVertices(entity);
74757 addChildVertices(entity);
74763 function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
74764 var wireframe = context.surface().classed('fill-wireframe');
74765 var visualDiff = context.surface().classed('highlight-edited');
74766 var zoom = geoScaleToZoom(projection.scale());
74767 var mode = context.mode();
74768 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74769 var base = context.history().base();
74770 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
74771 var touchLayer = selection.selectAll('.layer-touch.points');
74774 _currPersistent = {};
74776 } // Collect important vertices from the `entities` list..
74777 // (during a partial redraw, it will not contain everything)
74780 for (var i = 0; i < entities.length; i++) {
74781 var entity = entities[i];
74782 var geometry = entity.geometry(graph);
74783 var keep = false; // a point that looks like a vertex..
74785 if (geometry === 'point' && renderAsVertex(entity, graph, wireframe, zoom)) {
74786 _currPersistent[entity.id] = entity;
74787 keep = true; // a vertex of some importance..
74788 } else if (geometry === 'vertex' && (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || visualDiff && isEditedNode(entity, base, graph))) {
74789 _currPersistent[entity.id] = entity;
74791 } // whatever this is, it's not a persistent vertex..
74794 if (!keep && !fullRedraw) {
74795 delete _currPersistent[entity.id];
74797 } // 3 sets of vertices to consider:
74801 persistent: _currPersistent,
74802 // persistent = important vertices (render always)
74803 selected: _currSelected,
74804 // selected + siblings of selected (render always)
74805 hovered: _currHover // hovered + siblings of hovered (render only in draw modes)
74808 var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent); // Draw the vertices..
74809 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
74810 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
74812 var filterRendered = function filterRendered(d) {
74813 return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
74816 drawLayer.call(draw, graph, currentVisible(all), sets, filterRendered); // Draw touch targets..
74817 // When drawing, render all targets (not just those affected by a partial redraw)
74819 var filterTouch = function filterTouch(d) {
74820 return isMoving ? true : filterRendered(d);
74823 touchLayer.call(drawTargets, graph, currentVisible(all), filterTouch);
74825 function currentVisible(which) {
74826 return Object.keys(which).map(graph.hasEntity, graph) // the current version of this entity
74827 .filter(function (entity) {
74828 return entity && entity.intersects(extent, graph);
74831 } // partial redraw - only update the selected items..
74834 drawVertices.drawSelected = function (selection, graph, extent) {
74835 var wireframe = context.surface().classed('fill-wireframe');
74836 var zoom = geoScaleToZoom(projection.scale());
74837 _prevSelected = _currSelected || {};
74839 if (context.map().isInWideSelection()) {
74840 _currSelected = {};
74841 context.selectedIDs().forEach(function (id) {
74842 var entity = graph.hasEntity(id);
74843 if (!entity) return;
74845 if (entity.type === 'node') {
74846 if (renderAsVertex(entity, graph, wireframe, zoom)) {
74847 _currSelected[entity.id] = entity;
74852 _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
74853 } // note that drawVertices will add `_currSelected` automatically if needed..
74856 var filter = function filter(d) {
74857 return d.id in _prevSelected;
74860 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
74861 }; // partial redraw - only update the hovered items..
74864 drawVertices.drawHover = function (selection, graph, target, extent) {
74865 if (target === _currHoverTarget) return; // continue only if something changed
74867 var wireframe = context.surface().classed('fill-wireframe');
74868 var zoom = geoScaleToZoom(projection.scale());
74869 _prevHover = _currHover || {};
74870 _currHoverTarget = target;
74871 var entity = target && target.properties && target.properties.entity;
74874 _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
74877 } // note that drawVertices will add `_currHover` automatically if needed..
74880 var filter = function filter(d) {
74881 return d.id in _prevHover;
74884 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
74887 return drawVertices;
74890 function utilBindOnce(target, type, listener, capture) {
74891 var typeOnce = type + '.once';
74894 target.on(typeOnce, null);
74895 listener.apply(this, arguments);
74898 target.on(typeOnce, one, capture);
74902 function defaultFilter$2(d3_event) {
74903 return !d3_event.ctrlKey && !d3_event.button;
74906 function defaultExtent$1() {
74909 if (e instanceof SVGElement) {
74910 e = e.ownerSVGElement || e;
74912 if (e.hasAttribute('viewBox')) {
74913 e = e.viewBox.baseVal;
74914 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
74917 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
74920 return [[0, 0], [e.clientWidth, e.clientHeight]];
74923 function defaultWheelDelta$1(d3_event) {
74924 return -d3_event.deltaY * (d3_event.deltaMode === 1 ? 0.05 : d3_event.deltaMode ? 1 : 0.002);
74927 function defaultConstrain$1(transform, extent, translateExtent) {
74928 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
74929 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
74930 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
74931 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
74932 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));
74935 function utilZoomPan() {
74936 var filter = defaultFilter$2,
74937 extent = defaultExtent$1,
74938 constrain = defaultConstrain$1,
74939 wheelDelta = defaultWheelDelta$1,
74940 scaleExtent = [0, Infinity],
74941 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
74942 interpolate = interpolateZoom,
74943 dispatch$1 = dispatch('start', 'zoom', 'end'),
74945 _transform = identity$2,
74948 function zoom(selection) {
74949 selection.on('pointerdown.zoom', pointerdown).on('wheel.zoom', wheeled).style('touch-action', 'none').style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
74950 select(window).on('pointermove.zoompan', pointermove).on('pointerup.zoompan pointercancel.zoompan', pointerup);
74953 zoom.transform = function (collection, transform, point) {
74954 var selection = collection.selection ? collection.selection() : collection;
74956 if (collection !== selection) {
74957 schedule(collection, transform, point);
74959 selection.interrupt().each(function () {
74960 gesture(this, arguments).start(null).zoom(null, null, typeof transform === 'function' ? transform.apply(this, arguments) : transform).end(null);
74965 zoom.scaleBy = function (selection, k, p) {
74966 zoom.scaleTo(selection, function () {
74967 var k0 = _transform.k,
74968 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
74973 zoom.scaleTo = function (selection, k, p) {
74974 zoom.transform(selection, function () {
74975 var e = extent.apply(this, arguments),
74977 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
74978 p1 = t0.invert(p0),
74979 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
74980 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
74984 zoom.translateBy = function (selection, x, y) {
74985 zoom.transform(selection, function () {
74986 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);
74990 zoom.translateTo = function (selection, x, y, p) {
74991 zoom.transform(selection, function () {
74992 var e = extent.apply(this, arguments),
74994 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
74995 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);
74999 function scale(transform, k) {
75000 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
75001 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
75004 function translate(transform, p0, p1) {
75005 var x = p0[0] - p1[0] * transform.k,
75006 y = p0[1] - p1[1] * transform.k;
75007 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
75010 function centroid(extent) {
75011 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
75014 function schedule(transition, transform, point) {
75015 transition.on('start.zoom', function () {
75016 gesture(this, arguments).start(null);
75017 }).on('interrupt.zoom end.zoom', function () {
75018 gesture(this, arguments).end(null);
75019 }).tween('zoom', function () {
75022 g = gesture(that, args),
75023 e = extent.apply(that, args),
75024 p = !point ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
75025 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
75027 b = typeof transform === 'function' ? transform.apply(that, args) : transform,
75028 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
75029 return function (t) {
75030 if (t === 1) t = b; // Avoid rounding error on end.
75034 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
75036 g.zoom(null, null, t);
75041 function gesture(that, args, clean) {
75042 return !clean && _activeGesture || new Gesture(that, args);
75045 function Gesture(that, args) {
75049 this.extent = extent.apply(that, args);
75052 Gesture.prototype = {
75053 start: function start(d3_event) {
75054 if (++this.active === 1) {
75055 _activeGesture = this;
75056 dispatch$1.call('start', this, d3_event);
75061 zoom: function zoom(d3_event, key, transform) {
75062 if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
75063 if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
75064 if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
75065 _transform = transform;
75066 dispatch$1.call('zoom', this, d3_event, key, transform);
75069 end: function end(d3_event) {
75070 if (--this.active === 0) {
75071 _activeGesture = null;
75072 dispatch$1.call('end', this, d3_event);
75079 function wheeled(d3_event) {
75080 if (!filter.apply(this, arguments)) return;
75081 var g = gesture(this, arguments),
75083 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
75084 p = utilFastMouse(this)(d3_event); // If the mouse is in the same location as before, reuse it.
75085 // If there were recent wheel events, reset the wheel idle timeout.
75088 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
75089 g.mouse[1] = t.invert(g.mouse[0] = p);
75092 clearTimeout(g.wheel); // Otherwise, capture the mouse point and location at the start.
75094 g.mouse = [p, t.invert(p)];
75099 d3_event.preventDefault();
75100 d3_event.stopImmediatePropagation();
75101 g.wheel = setTimeout(wheelidled, _wheelDelay);
75102 g.zoom(d3_event, 'mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
75104 function wheelidled() {
75110 var _downPointerIDs = new Set();
75112 var _pointerLocGetter;
75114 function pointerdown(d3_event) {
75115 _downPointerIDs.add(d3_event.pointerId);
75117 if (!filter.apply(this, arguments)) return;
75118 var g = gesture(this, arguments, _downPointerIDs.size === 1);
75120 d3_event.stopImmediatePropagation();
75121 _pointerLocGetter = utilFastMouse(this);
75123 var loc = _pointerLocGetter(d3_event);
75125 var p = [loc, _transform.invert(loc), d3_event.pointerId];
75130 } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
75140 function pointermove(d3_event) {
75141 if (!_downPointerIDs.has(d3_event.pointerId)) return;
75142 if (!_activeGesture || !_pointerLocGetter) return;
75143 var g = gesture(this, arguments);
75144 var isPointer0 = g.pointer0 && g.pointer0[2] === d3_event.pointerId;
75145 var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === d3_event.pointerId;
75147 if ((isPointer0 || isPointer1) && 'buttons' in d3_event && !d3_event.buttons) {
75148 // The pointer went up without ending the gesture somehow, e.g.
75149 // a down mouse was moved off the map and released. End it here.
75150 if (g.pointer0) _downPointerIDs["delete"](g.pointer0[2]);
75151 if (g.pointer1) _downPointerIDs["delete"](g.pointer1[2]);
75156 d3_event.preventDefault();
75157 d3_event.stopImmediatePropagation();
75159 var loc = _pointerLocGetter(d3_event);
75162 if (isPointer0) g.pointer0[0] = loc;else if (isPointer1) g.pointer1[0] = loc;
75166 var p0 = g.pointer0[0],
75167 l0 = g.pointer0[1],
75168 p1 = g.pointer1[0],
75169 l1 = g.pointer1[1],
75170 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
75171 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
75172 t = scale(t, Math.sqrt(dp / dl));
75173 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
75174 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
75175 } else if (g.pointer0) {
75180 g.zoom(d3_event, 'touch', constrain(translate(t, p, l), g.extent, translateExtent));
75183 function pointerup(d3_event) {
75184 if (!_downPointerIDs.has(d3_event.pointerId)) return;
75186 _downPointerIDs["delete"](d3_event.pointerId);
75188 if (!_activeGesture) return;
75189 var g = gesture(this, arguments);
75190 d3_event.stopImmediatePropagation();
75191 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;
75193 if (g.pointer1 && !g.pointer0) {
75194 g.pointer0 = g.pointer1;
75198 if (g.pointer0) g.pointer0[1] = _transform.invert(g.pointer0[0]);else {
75203 zoom.wheelDelta = function (_) {
75204 return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
75207 zoom.filter = function (_) {
75208 return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
75211 zoom.extent = function (_) {
75212 return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
75215 zoom.scaleExtent = function (_) {
75216 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
75219 zoom.translateExtent = function (_) {
75220 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]]];
75223 zoom.constrain = function (_) {
75224 return arguments.length ? (constrain = _, zoom) : constrain;
75227 zoom.interpolate = function (_) {
75228 return arguments.length ? (interpolate = _, zoom) : interpolate;
75231 zoom._transform = function (_) {
75232 return arguments.length ? (_transform = _, zoom) : _transform;
75235 return utilRebind(zoom, dispatch$1, 'on');
75238 // if pointer events are supported. Falls back to default `dblclick` event.
75240 function utilDoubleUp() {
75241 var dispatch$1 = dispatch('doubleUp');
75242 var _maxTimespan = 500; // milliseconds
75244 var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
75246 var _pointer; // object representing the pointer that could trigger double up
75249 function pointerIsValidFor(loc) {
75250 // second pointerup must occur within a small timeframe after the first pointerdown
75251 return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown
75252 geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
75255 function pointerdown(d3_event) {
75256 // ignore right-click
75257 if (d3_event.ctrlKey || d3_event.button === 2) return;
75258 var loc = [d3_event.clientX, d3_event.clientY]; // Don't rely on pointerId here since it can change between pointerdown
75259 // events on touch devices
75261 if (_pointer && !pointerIsValidFor(loc)) {
75262 // if this pointer is no longer valid, clear it so another can be started
75263 _pointer = undefined;
75269 startTime: new Date().getTime(),
75271 pointerId: d3_event.pointerId
75275 _pointer.pointerId = d3_event.pointerId;
75279 function pointerup(d3_event) {
75280 // ignore right-click
75281 if (d3_event.ctrlKey || d3_event.button === 2) return;
75282 if (!_pointer || _pointer.pointerId !== d3_event.pointerId) return;
75283 _pointer.upCount += 1;
75285 if (_pointer.upCount === 2) {
75287 var loc = [d3_event.clientX, d3_event.clientY];
75289 if (pointerIsValidFor(loc)) {
75290 var locInThis = utilFastMouse(this)(d3_event);
75291 dispatch$1.call('doubleUp', this, d3_event, locInThis);
75292 } // clear the pointer info in any case
75295 _pointer = undefined;
75299 function doubleUp(selection) {
75300 if ('PointerEvent' in window) {
75301 // dblclick isn't well supported on touch devices so manually use
75302 // pointer events if they're available
75303 selection.on('pointerdown.doubleUp', pointerdown).on('pointerup.doubleUp', pointerup);
75305 // fallback to dblclick
75306 selection.on('dblclick.doubleUp', function (d3_event) {
75307 dispatch$1.call('doubleUp', this, d3_event, utilFastMouse(this)(d3_event));
75312 doubleUp.off = function (selection) {
75313 selection.on('pointerdown.doubleUp', null).on('pointerup.doubleUp', null).on('dblclick.doubleUp', null);
75316 return utilRebind(doubleUp, dispatch$1, 'on');
75319 var TILESIZE = 256;
75322 var kMin = geoZoomToScale(minZoom, TILESIZE);
75323 var kMax = geoZoomToScale(maxZoom, TILESIZE);
75325 function clamp(num, min, max) {
75326 return Math.max(min, Math.min(num, max));
75329 function rendererMap(context) {
75330 var dispatch$1 = dispatch('move', 'drawn', 'crossEditableZoom', 'hitMinZoom', 'changeHighlighting', 'changeAreaFill');
75331 var projection = context.projection;
75332 var curtainProjection = context.curtainProjection;
75341 var _selection = select(null);
75343 var supersurface = select(null);
75344 var wrapper = select(null);
75345 var surface = select(null);
75346 var _dimensions = [1, 1];
75347 var _dblClickZoomEnabled = true;
75348 var _redrawEnabled = true;
75350 var _gestureTransformStart;
75352 var _transformStart = projection.transform();
75354 var _transformLast;
75356 var _isTransformed = false;
75359 var _getMouseCoords;
75361 var _lastPointerEvent;
75363 var _lastWithinEditableZoom; // whether a pointerdown event started the zoom
75366 var _pointerDown = false; // use pointer events on supported platforms; fallback to mouse events
75368 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
75371 var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
75373 var _zoomerPanner = _zoomerPannerFunction().scaleExtent([kMin, kMax]).interpolate(interpolate).filter(zoomEventFilter).on('zoom.map', zoomPan).on('start.map', function (d3_event) {
75374 _pointerDown = d3_event && (d3_event.type === 'pointerdown' || d3_event.sourceEvent && d3_event.sourceEvent.type === 'pointerdown');
75375 }).on('end.map', function () {
75376 _pointerDown = false;
75379 var _doubleUpHandler = utilDoubleUp();
75381 var scheduleRedraw = throttle(redraw, 750); // var isRedrawScheduled = false;
75382 // var pendingRedrawCall;
75383 // function scheduleRedraw() {
75384 // // Only schedule the redraw if one has not already been set.
75385 // if (isRedrawScheduled) return;
75386 // isRedrawScheduled = true;
75387 // var that = this;
75388 // var args = arguments;
75389 // pendingRedrawCall = window.requestIdleCallback(function () {
75390 // // Reset the boolean so future redraws can be set.
75391 // isRedrawScheduled = false;
75392 // redraw.apply(that, args);
75393 // }, { timeout: 1400 });
75397 function cancelPendingRedraw() {
75398 scheduleRedraw.cancel(); // isRedrawScheduled = false;
75399 // window.cancelIdleCallback(pendingRedrawCall);
75402 function map(selection) {
75403 _selection = selection;
75404 context.on('change.map', immediateRedraw);
75405 var osm = context.connection();
75408 osm.on('change.map', immediateRedraw);
75411 function didUndoOrRedo(targetTransform) {
75412 var mode = context.mode().id;
75413 if (mode !== 'browse' && mode !== 'select') return;
75415 if (targetTransform) {
75416 map.transformEase(targetTransform);
75420 context.history().on('merge.map', function () {
75422 }).on('change.map', immediateRedraw).on('undone.map', function (stack, fromStack) {
75423 didUndoOrRedo(fromStack.transform);
75424 }).on('redone.map', function (stack) {
75425 didUndoOrRedo(stack.transform);
75427 context.background().on('change.map', immediateRedraw);
75428 context.features().on('redraw.map', immediateRedraw);
75429 drawLayers.on('change.map', function () {
75430 context.background().updateImagery();
75433 selection.on('wheel.map mousewheel.map', function (d3_event) {
75434 // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
75435 d3_event.preventDefault();
75436 }).call(_zoomerPanner).call(_zoomerPanner.transform, projection.transform()).on('dblclick.zoom', null); // override d3-zoom dblclick handling
75438 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
75439 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
75441 wrapper = supersurface.append('div').attr('class', 'layer layer-data');
75442 map.surface = surface = wrapper.call(drawLayers).selectAll('.surface');
75443 surface.call(drawLabels.observe).call(_doubleUpHandler).on(_pointerPrefix + 'down.zoom', function (d3_event) {
75444 _lastPointerEvent = d3_event;
75446 if (d3_event.button === 2) {
75447 d3_event.stopPropagation();
75449 }, true).on(_pointerPrefix + 'up.zoom', function (d3_event) {
75450 _lastPointerEvent = d3_event;
75452 if (resetTransform()) {
75455 }).on(_pointerPrefix + 'move.map', function (d3_event) {
75456 _lastPointerEvent = d3_event;
75457 }).on(_pointerPrefix + 'over.vertices', function (d3_event) {
75458 if (map.editableDataEnabled() && !_isTransformed) {
75459 var hover = d3_event.target.__data__;
75460 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
75461 dispatch$1.call('drawn', this, {
75465 }).on(_pointerPrefix + 'out.vertices', function (d3_event) {
75466 if (map.editableDataEnabled() && !_isTransformed) {
75467 var hover = d3_event.relatedTarget && d3_event.relatedTarget.__data__;
75468 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
75469 dispatch$1.call('drawn', this, {
75474 var detected = utilDetect(); // only WebKit supports gesture events
75476 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
75477 // but we only need to do this on desktop Safari anyway. – #7694
75478 !detected.isMobileWebKit) {
75479 // Desktop Safari sends gesture events for multitouch trackpad pinches.
75480 // We can listen for these and translate them into map zooms.
75481 surface.on('gesturestart.surface', function (d3_event) {
75482 d3_event.preventDefault();
75483 _gestureTransformStart = projection.transform();
75484 }).on('gesturechange.surface', gestureChange);
75485 } // must call after surface init
75490 _doubleUpHandler.on('doubleUp.map', function (d3_event, p0) {
75491 if (!_dblClickZoomEnabled) return; // don't zoom if targeting something other than the map itself
75493 if (_typeof(d3_event.target.__data__) === 'object' && // or area fills
75494 !select(d3_event.target).classed('fill')) return;
75495 var zoomOut = d3_event.shiftKey;
75496 var t = projection.transform();
75497 var p1 = t.invert(p0);
75498 t = t.scale(zoomOut ? 0.5 : 2);
75499 t.x = p0[0] - p1[0] * t.k;
75500 t.y = p0[1] - p1[1] * t.k;
75501 map.transformEase(t);
75504 context.on('enter.map', function () {
75505 if (!map.editableDataEnabled(true
75506 /* skip zoom check */
75507 )) return; // redraw immediately any objects affected by a change in selectedIDs.
75509 var graph = context.graph();
75510 var selectedAndParents = {};
75511 context.selectedIDs().forEach(function (id) {
75512 var entity = graph.hasEntity(id);
75515 selectedAndParents[entity.id] = entity;
75517 if (entity.type === 'node') {
75518 graph.parentWays(entity).forEach(function (parent) {
75519 selectedAndParents[parent.id] = parent;
75524 var data = Object.values(selectedAndParents);
75526 var filter = function filter(d) {
75527 return d.id in selectedAndParents;
75530 data = context.features().filter(data, graph);
75531 surface.call(drawVertices.drawSelected, graph, map.extent()).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent());
75532 dispatch$1.call('drawn', this, {
75534 }); // redraw everything else later
75538 map.dimensions(utilGetDimensions(selection));
75541 function zoomEventFilter(d3_event) {
75542 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
75543 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
75544 // This can happen if a previous `mousedown` occurred without a `mouseup`.
75545 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
75546 // so that d3-zoom won't stop propagation of new `mousedown` events.
75547 if (d3_event.type === 'mousedown') {
75548 var hasOrphan = false;
75549 var listeners = window.__on;
75551 for (var i = 0; i < listeners.length; i++) {
75552 var listener = listeners[i];
75554 if (listener.name === 'zoom' && listener.type === 'mouseup') {
75561 var event = window.CustomEvent;
75564 event = new event('mouseup');
75566 event = window.document.createEvent('Event');
75567 event.initEvent('mouseup', false, false);
75568 } // Event needs to be dispatched with an event.view property.
75571 event.view = window;
75572 window.dispatchEvent(event);
75576 return d3_event.button !== 2; // ignore right clicks
75579 function pxCenter() {
75580 return [_dimensions[0] / 2, _dimensions[1] / 2];
75583 function drawEditable(difference, extent) {
75584 var mode = context.mode();
75585 var graph = context.graph();
75586 var features = context.features();
75587 var all = context.history().intersects(map.extent());
75588 var fullRedraw = false;
75592 var applyFeatureLayerFilters = true;
75594 if (map.isInWideSelection()) {
75596 utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function (id) {
75597 var entity = context.hasEntity(id);
75598 if (entity) data.push(entity);
75601 filter = utilFunctor(true); // selected features should always be visible, so we can skip filtering
75603 applyFeatureLayerFilters = false;
75604 } else if (difference) {
75605 var complete = difference.complete(map.extent());
75606 data = Object.values(complete).filter(Boolean);
75607 set = new Set(Object.keys(complete));
75609 filter = function filter(d) {
75610 return set.has(d.id);
75613 features.clear(data);
75615 // force a full redraw if gatherStats detects that a feature
75616 // should be auto-hidden (e.g. points or buildings)..
75617 if (features.gatherStats(all, graph, _dimensions)) {
75618 extent = undefined;
75622 data = context.history().intersects(map.extent().intersection(extent));
75623 set = new Set(data.map(function (entity) {
75627 filter = function filter(d) {
75628 return set.has(d.id);
75633 filter = utilFunctor(true);
75637 if (applyFeatureLayerFilters) {
75638 data = features.filter(data, graph);
75640 context.features().resetStats();
75643 if (mode && mode.id === 'select') {
75644 // update selected vertices - the user might have just double-clicked a way,
75645 // creating a new vertex, triggering a partial redraw without a mode change
75646 surface.call(drawVertices.drawSelected, graph, map.extent());
75649 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);
75650 dispatch$1.call('drawn', this, {
75655 map.init = function () {
75656 drawLayers = svgLayers(projection, context);
75657 drawPoints = svgPoints(projection, context);
75658 drawVertices = svgVertices(projection, context);
75659 drawLines = svgLines(projection, context);
75660 drawAreas = svgAreas(projection, context);
75661 drawMidpoints = svgMidpoints(projection, context);
75662 drawLabels = svgLabels(projection, context);
75665 function editOff() {
75666 context.features().resetStats();
75667 surface.selectAll('.layer-osm *').remove();
75668 surface.selectAll('.layer-touch:not(.markers) *').remove();
75672 'select-note': true,
75673 'select-data': true,
75674 'select-error': true
75676 var mode = context.mode();
75678 if (mode && !allowed[mode.id]) {
75679 context.enter(modeBrowse(context));
75682 dispatch$1.call('drawn', this, {
75687 function gestureChange(d3_event) {
75688 // Remap Safari gesture events to wheel events - #5492
75689 // We want these disabled most places, but enabled for zoom/unzoom on map surface
75690 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
75692 e.preventDefault();
75695 // dummy values to ignore in zoomPan
75697 // dummy values to ignore in zoomPan
75698 clientX: e.clientX,
75699 clientY: e.clientY,
75700 screenX: e.screenX,
75701 screenY: e.screenY,
75705 var e2 = new WheelEvent('wheel', props);
75706 e2._scale = e.scale; // preserve the original scale
75708 e2._rotation = e.rotation; // preserve the original rotation
75710 _selection.node().dispatchEvent(e2);
75713 function zoomPan(event, key, transform) {
75714 var source = event && event.sourceEvent || event;
75715 var eventTransform = transform || event && event.transform;
75716 var x = eventTransform.x;
75717 var y = eventTransform.y;
75718 var k = eventTransform.k; // Special handling of 'wheel' events:
75719 // They might be triggered by the user scrolling the mouse wheel,
75720 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
75722 if (source && source.type === 'wheel') {
75723 // assume that the gesture is already handled by pointer events
75724 if (_pointerDown) return;
75725 var detected = utilDetect();
75726 var dX = source.deltaX;
75727 var dY = source.deltaY;
75731 var t0, p0, p1; // Normalize mousewheel scroll speed (Firefox) - #3029
75732 // If wheel delta is provided in LINE units, recalculate it in PIXEL units
75733 // We are essentially redoing the calculations that occur here:
75734 // https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
75735 // See this for more info:
75736 // https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
75738 if (source.deltaMode === 1
75741 // Convert from lines to pixels, more if the user is scrolling fast.
75742 // (I made up the exp function to roughly match Firefox to what Chrome does)
75743 // These numbers should be floats, because integers are treated as pan gesture below.
75744 var lines = Math.abs(source.deltaY);
75745 var sign = source.deltaY > 0 ? 1 : -1;
75746 dY = sign * clamp(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, // min
75747 350.000244140625 // max
75748 ); // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
75749 // There doesn't seem to be any scroll acceleration.
75750 // This multiplier increases the speed a little bit - #5512
75752 if (detected.os !== 'mac') {
75754 } // recalculate x2,y2,k2
75757 t0 = _isTransformed ? _transformLast : _transformStart;
75758 p0 = _getMouseCoords(source);
75759 p1 = t0.invert(p0);
75760 k2 = t0.k * Math.pow(2, -dY / 500);
75761 k2 = clamp(k2, kMin, kMax);
75762 x2 = p0[0] - p1[0] * k2;
75763 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (Safari) - #5492
75764 // These are fake `wheel` events we made from Safari `gesturechange` events..
75765 } else if (source._scale) {
75766 // recalculate x2,y2,k2
75767 t0 = _gestureTransformStart;
75768 p0 = _getMouseCoords(source);
75769 p1 = t0.invert(p0);
75770 k2 = t0.k * source._scale;
75771 k2 = clamp(k2, kMin, kMax);
75772 x2 = p0[0] - p1[0] * k2;
75773 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (all browsers except Safari) - #5492
75774 // Pinch zooming via the `wheel` event will always have:
75775 // - `ctrlKey = true`
75776 // - `deltaY` is not round integer pixels (ignore `deltaX`)
75777 } else if (source.ctrlKey && !isInteger(dY)) {
75778 dY *= 6; // slightly scale up whatever the browser gave us
75779 // recalculate x2,y2,k2
75781 t0 = _isTransformed ? _transformLast : _transformStart;
75782 p0 = _getMouseCoords(source);
75783 p1 = t0.invert(p0);
75784 k2 = t0.k * Math.pow(2, -dY / 500);
75785 k2 = clamp(k2, kMin, kMax);
75786 x2 = p0[0] - p1[0] * k2;
75787 y2 = p0[1] - p1[1] * k2; // Trackpad scroll zooming with shift or alt/option key down
75788 } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
75789 // recalculate x2,y2,k2
75790 t0 = _isTransformed ? _transformLast : _transformStart;
75791 p0 = _getMouseCoords(source);
75792 p1 = t0.invert(p0);
75793 k2 = t0.k * Math.pow(2, -dY / 500);
75794 k2 = clamp(k2, kMin, kMax);
75795 x2 = p0[0] - p1[0] * k2;
75796 y2 = p0[1] - p1[1] * k2; // 2 finger map panning (Mac only, all browsers) - #5492, #5512
75797 // Panning via the `wheel` event will always have:
75798 // - `ctrlKey = false`
75799 // - `deltaX`,`deltaY` are round integer pixels
75800 } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
75801 p1 = projection.translate();
75804 k2 = projection.scale();
75805 k2 = clamp(k2, kMin, kMax);
75806 } // something changed - replace the event transform
75809 if (x2 !== x || y2 !== y || k2 !== k) {
75813 eventTransform = identity$2.translate(x2, y2).scale(k2);
75815 if (_zoomerPanner._transform) {
75816 // utilZoomPan interface
75817 _zoomerPanner._transform(eventTransform);
75819 // d3_zoom interface
75820 _selection.node().__zoom = eventTransform;
75825 if (_transformStart.x === x && _transformStart.y === y && _transformStart.k === k) {
75826 return; // no change
75829 var withinEditableZoom = map.withinEditableZoom();
75831 if (_lastWithinEditableZoom !== withinEditableZoom) {
75832 if (_lastWithinEditableZoom !== undefined) {
75833 // notify that the map zoomed in or out over the editable zoom threshold
75834 dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
75837 _lastWithinEditableZoom = withinEditableZoom;
75840 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
75841 surface.interrupt();
75842 dispatch$1.call('hitMinZoom', this, map);
75843 setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
75845 dispatch$1.call('move', this, map);
75849 projection.transform(eventTransform);
75850 var scale = k / _transformStart.k;
75851 var tX = (x / scale - _transformStart.x) * scale;
75852 var tY = (y / scale - _transformStart.y) * scale;
75854 if (context.inIntro()) {
75855 curtainProjection.transform({
75863 _lastPointerEvent = event;
75866 _isTransformed = true;
75867 _transformLast = eventTransform;
75868 utilSetTransform(supersurface, tX, tY, scale);
75870 dispatch$1.call('move', this, map);
75872 function isInteger(val) {
75873 return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
75877 function resetTransform() {
75878 if (!_isTransformed) return false;
75879 utilSetTransform(supersurface, 0, 0);
75880 _isTransformed = false;
75882 if (context.inIntro()) {
75883 curtainProjection.transform(projection.transform());
75889 function redraw(difference, extent) {
75890 if (surface.empty() || !_redrawEnabled) return; // If we are in the middle of a zoom/pan, we can't do differenced redraws.
75891 // It would result in artifacts where differenced entities are redrawn with
75892 // one transform and unchanged entities with another.
75894 if (resetTransform()) {
75895 difference = extent = undefined;
75898 var zoom = map.zoom();
75899 var z = String(~~zoom);
75901 if (surface.attr('data-zoom') !== z) {
75902 surface.attr('data-zoom', z);
75903 } // class surface as `lowzoom` around z17-z18.5 (based on latitude)
75906 var lat = map.center()[1];
75907 var lowzoom = linear$2().domain([-60, 0, 60]).range([17, 18.5, 17]).clamp(true);
75908 surface.classed('low-zoom', zoom <= lowzoom(lat));
75911 supersurface.call(context.background());
75912 wrapper.call(drawLayers);
75916 if (map.editableDataEnabled() || map.isInWideSelection()) {
75917 context.loadTiles(projection);
75918 drawEditable(difference, extent);
75923 _transformStart = projection.transform();
75927 var immediateRedraw = function immediateRedraw(difference, extent) {
75928 if (!difference && !extent) cancelPendingRedraw();
75929 redraw(difference, extent);
75932 map.lastPointerEvent = function () {
75933 return _lastPointerEvent;
75936 map.mouse = function (d3_event) {
75937 var event = _lastPointerEvent || d3_event;
75942 while (s = event.sourceEvent) {
75946 return _getMouseCoords(event);
75950 }; // returns Lng/Lat
75953 map.mouseCoordinates = function () {
75954 var coord = map.mouse() || pxCenter();
75955 return projection.invert(coord);
75958 map.dblclickZoomEnable = function (val) {
75959 if (!arguments.length) return _dblClickZoomEnabled;
75960 _dblClickZoomEnabled = val;
75964 map.redrawEnable = function (val) {
75965 if (!arguments.length) return _redrawEnabled;
75966 _redrawEnabled = val;
75970 map.isTransformed = function () {
75971 return _isTransformed;
75974 function setTransform(t2, duration, force) {
75975 var t = projection.transform();
75976 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
75979 _selection.transition().duration(duration).on('start', function () {
75981 }).call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
75983 projection.transform(t2);
75984 _transformStart = t2;
75986 _selection.call(_zoomerPanner.transform, _transformStart);
75992 function setCenterZoom(loc2, z2, duration, force) {
75993 var c = map.center();
75994 var z = map.zoom();
75995 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
75996 var proj = geoRawMercator().transform(projection.transform()); // copy projection
75998 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
76000 var t = proj.translate();
76001 var point = proj(loc2);
76002 var center = pxCenter();
76003 t[0] += center[0] - point[0];
76004 t[1] += center[1] - point[1];
76005 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
76008 map.pan = function (delta, duration) {
76009 var t = projection.translate();
76010 var k = projection.scale();
76015 _selection.transition().duration(duration).on('start', function () {
76017 }).call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
76019 projection.translate(t);
76020 _transformStart = projection.transform();
76022 _selection.call(_zoomerPanner.transform, _transformStart);
76024 dispatch$1.call('move', this, map);
76031 map.dimensions = function (val) {
76032 if (!arguments.length) return _dimensions;
76034 drawLayers.dimensions(_dimensions);
76035 context.background().dimensions(_dimensions);
76036 projection.clipExtent([[0, 0], _dimensions]);
76037 _getMouseCoords = utilFastMouse(supersurface.node());
76042 function zoomIn(delta) {
76043 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
76046 function zoomOut(delta) {
76047 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
76050 map.zoomIn = function () {
76054 map.zoomInFurther = function () {
76058 map.canZoomIn = function () {
76059 return map.zoom() < maxZoom;
76062 map.zoomOut = function () {
76066 map.zoomOutFurther = function () {
76070 map.canZoomOut = function () {
76071 return map.zoom() > minZoom;
76074 map.center = function (loc2) {
76075 if (!arguments.length) {
76076 return projection.invert(pxCenter());
76079 if (setCenterZoom(loc2, map.zoom())) {
76080 dispatch$1.call('move', this, map);
76087 map.unobscuredCenterZoomEase = function (loc, zoom) {
76088 var offset = map.unobscuredOffsetPx();
76089 var proj = geoRawMercator().transform(projection.transform()); // copy projection
76090 // use the target zoom to calculate the offset center
76092 proj.scale(geoZoomToScale(zoom, TILESIZE));
76093 var locPx = proj(loc);
76094 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
76095 var offsetLoc = proj.invert(offsetLocPx);
76096 map.centerZoomEase(offsetLoc, zoom);
76099 map.unobscuredOffsetPx = function () {
76100 var openPane = context.container().select('.map-panes .map-pane.shown');
76102 if (!openPane.empty()) {
76103 return [openPane.node().offsetWidth / 2, 0];
76109 map.zoom = function (z2) {
76110 if (!arguments.length) {
76111 return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
76114 if (z2 < _minzoom) {
76115 surface.interrupt();
76116 dispatch$1.call('hitMinZoom', this, map);
76117 z2 = context.minEditableZoom();
76120 if (setCenterZoom(map.center(), z2)) {
76121 dispatch$1.call('move', this, map);
76128 map.centerZoom = function (loc2, z2) {
76129 if (setCenterZoom(loc2, z2)) {
76130 dispatch$1.call('move', this, map);
76137 map.zoomTo = function (entity) {
76138 var extent = entity.extent(context.graph());
76139 if (!isFinite(extent.area())) return map;
76140 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
76141 return map.centerZoom(extent.center(), z2);
76144 map.centerEase = function (loc2, duration) {
76145 duration = duration || 250;
76146 setCenterZoom(loc2, map.zoom(), duration);
76150 map.zoomEase = function (z2, duration) {
76151 duration = duration || 250;
76152 setCenterZoom(map.center(), z2, duration, false);
76156 map.centerZoomEase = function (loc2, z2, duration) {
76157 duration = duration || 250;
76158 setCenterZoom(loc2, z2, duration, false);
76162 map.transformEase = function (t2, duration) {
76163 duration = duration || 250;
76164 setTransform(t2, duration, false
76170 map.zoomToEase = function (obj, duration) {
76173 if (Array.isArray(obj)) {
76174 obj.forEach(function (entity) {
76175 var entityExtent = entity.extent(context.graph());
76178 extent = entityExtent;
76180 extent = extent.extend(entityExtent);
76184 extent = obj.extent(context.graph());
76187 if (!isFinite(extent.area())) return map;
76188 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
76189 return map.centerZoomEase(extent.center(), z2, duration);
76192 map.startEase = function () {
76193 utilBindOnce(surface, _pointerPrefix + 'down.ease', function () {
76199 map.cancelEase = function () {
76200 _selection.interrupt();
76205 map.extent = function (val) {
76206 if (!arguments.length) {
76207 return new geoExtent(projection.invert([0, _dimensions[1]]), projection.invert([_dimensions[0], 0]));
76209 var extent = geoExtent(val);
76210 map.centerZoom(extent.center(), map.extentZoom(extent));
76214 map.trimmedExtent = function (val) {
76215 if (!arguments.length) {
76219 return new geoExtent(projection.invert([pad, _dimensions[1] - footerY - pad]), projection.invert([_dimensions[0] - pad, headerY + pad]));
76221 var extent = geoExtent(val);
76222 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
76226 function calcExtentZoom(extent, dim) {
76227 var tl = projection([extent[0][0], extent[1][1]]);
76228 var br = projection([extent[1][0], extent[0][1]]); // Calculate maximum zoom that fits extent
76230 var hFactor = (br[0] - tl[0]) / dim[0];
76231 var vFactor = (br[1] - tl[1]) / dim[1];
76232 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
76233 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
76234 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
76238 map.extentZoom = function (val) {
76239 return calcExtentZoom(geoExtent(val), _dimensions);
76242 map.trimmedExtentZoom = function (val) {
76245 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
76246 return calcExtentZoom(geoExtent(val), trimmed);
76249 map.withinEditableZoom = function () {
76250 return map.zoom() >= context.minEditableZoom();
76253 map.isInWideSelection = function () {
76254 return !map.withinEditableZoom() && context.selectedIDs().length;
76257 map.editableDataEnabled = function (skipZoomCheck) {
76258 var layer = context.layers().layer('osm');
76259 if (!layer || !layer.enabled()) return false;
76260 return skipZoomCheck || map.withinEditableZoom();
76263 map.notesEditable = function () {
76264 var layer = context.layers().layer('notes');
76265 if (!layer || !layer.enabled()) return false;
76266 return map.withinEditableZoom();
76269 map.minzoom = function (val) {
76270 if (!arguments.length) return _minzoom;
76275 map.toggleHighlightEdited = function () {
76276 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
76277 map.pan([0, 0]); // trigger a redraw
76279 dispatch$1.call('changeHighlighting', this);
76282 map.areaFillOptions = ['wireframe', 'partial', 'full'];
76284 map.activeAreaFill = function (val) {
76285 if (!arguments.length) return corePreferences('area-fill') || 'partial';
76286 corePreferences('area-fill', val);
76288 if (val !== 'wireframe') {
76289 corePreferences('area-fill-toggle', val);
76293 map.pan([0, 0]); // trigger a redraw
76295 dispatch$1.call('changeAreaFill', this);
76299 map.toggleWireframe = function () {
76300 var activeFill = map.activeAreaFill();
76302 if (activeFill === 'wireframe') {
76303 activeFill = corePreferences('area-fill-toggle') || 'partial';
76305 activeFill = 'wireframe';
76308 map.activeAreaFill(activeFill);
76311 function updateAreaFill() {
76312 var activeFill = map.activeAreaFill();
76313 map.areaFillOptions.forEach(function (opt) {
76314 surface.classed('fill-' + opt, Boolean(opt === activeFill));
76318 map.layers = function () {
76322 map.doubleUpHandler = function () {
76323 return _doubleUpHandler;
76326 return utilRebind(map, dispatch$1, 'on');
76329 function rendererPhotos(context) {
76330 var dispatch$1 = dispatch('change');
76331 var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
76332 var _allPhotoTypes = ['flat', 'panoramic'];
76334 var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
76337 var _dateFilters = ['fromDate', 'toDate'];
76345 function photos() {}
76347 function updateStorage() {
76348 if (window.mocha) return;
76349 var hash = utilStringQs(window.location.hash);
76350 var enabled = context.layers().all().filter(function (d) {
76351 return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
76352 }).map(function (d) {
76356 if (enabled.length) {
76357 hash.photo_overlay = enabled.join(',');
76359 delete hash.photo_overlay;
76362 window.location.replace('#' + utilQsString(hash, true));
76365 photos.overlayLayerIDs = function () {
76369 photos.allPhotoTypes = function () {
76370 return _allPhotoTypes;
76373 photos.dateFilters = function () {
76374 return _dateFilters;
76377 photos.dateFilterValue = function (val) {
76378 return val === _dateFilters[0] ? _fromDate : _toDate;
76381 photos.setDateFilter = function (type, val, updateUrl) {
76382 // validate the date
76383 var date = val && new Date(val);
76385 if (date && !isNaN(date)) {
76386 val = date.toISOString().substr(0, 10);
76391 if (type === _dateFilters[0]) {
76394 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
76395 _toDate = _fromDate;
76399 if (type === _dateFilters[1]) {
76402 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
76403 _fromDate = _toDate;
76407 dispatch$1.call('change', this);
76412 if (_fromDate || _toDate) {
76413 rangeString = (_fromDate || '') + '_' + (_toDate || '');
76416 setUrlFilterValue('photo_dates', rangeString);
76420 photos.setUsernameFilter = function (val, updateUrl) {
76421 if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(',');
76424 val = val.map(function (d) {
76426 }).filter(Boolean);
76434 dispatch$1.call('change', this);
76440 hashString = _usernames.join(',');
76443 setUrlFilterValue('photo_username', hashString);
76447 function setUrlFilterValue(property, val) {
76448 if (!window.mocha) {
76449 var hash = utilStringQs(window.location.hash);
76452 if (hash[property] === val) return;
76453 hash[property] = val;
76455 if (!(property in hash)) return;
76456 delete hash[property];
76459 window.location.replace('#' + utilQsString(hash, true));
76463 function showsLayer(id) {
76464 var layer = context.layers().layer(id);
76465 return layer && layer.supported() && layer.enabled();
76468 photos.shouldFilterByDate = function () {
76469 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
76472 photos.shouldFilterByPhotoType = function () {
76473 return showsLayer('mapillary') || showsLayer('streetside') && showsLayer('openstreetcam');
76476 photos.shouldFilterByUsername = function () {
76477 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
76480 photos.showsPhotoType = function (val) {
76481 if (!photos.shouldFilterByPhotoType()) return true;
76482 return _shownPhotoTypes.indexOf(val) !== -1;
76485 photos.showsFlat = function () {
76486 return photos.showsPhotoType('flat');
76489 photos.showsPanoramic = function () {
76490 return photos.showsPhotoType('panoramic');
76493 photos.fromDate = function () {
76497 photos.toDate = function () {
76501 photos.togglePhotoType = function (val) {
76502 var index = _shownPhotoTypes.indexOf(val);
76504 if (index !== -1) {
76505 _shownPhotoTypes.splice(index, 1);
76507 _shownPhotoTypes.push(val);
76510 dispatch$1.call('change', this);
76514 photos.usernames = function () {
76518 photos.init = function () {
76519 var hash = utilStringQs(window.location.hash);
76521 if (hash.photo_dates) {
76522 // expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators
76523 var parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim());
76524 this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false);
76525 this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false);
76528 if (hash.photo_username) {
76529 this.setUsernameFilter(hash.photo_username, false);
76532 if (hash.photo_overlay) {
76533 // support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=openstreetcam;mapillary;streetside`
76534 var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
76535 hashOverlayIDs.forEach(function (id) {
76536 var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id);
76537 if (layer && !layer.enabled()) layer.enabled(true);
76542 // support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ`
76543 var photoIds = hash.photo.replace(/;/g, ',').split(',');
76544 var photoId = photoIds.length && photoIds[0].trim();
76545 var results = /(.*)\/(.*)/g.exec(photoId);
76547 if (results && results.length >= 3) {
76548 var serviceId = results[1];
76549 var photoKey = results[2];
76550 var service = services[serviceId];
76552 if (service && service.ensureViewerLoaded) {
76553 // if we're showing a photo then make sure its layer is enabled too
76554 var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId);
76555 if (layer && !layer.enabled()) layer.enabled(true);
76556 var baselineTime = Date.now();
76557 service.on('loadedImages.rendererPhotos', function () {
76558 // don't open the viewer if too much time has elapsed
76559 if (Date.now() - baselineTime > 45000) {
76560 service.on('loadedImages.rendererPhotos', null);
76564 if (!service.cachedImage(photoKey)) return;
76565 service.on('loadedImages.rendererPhotos', null);
76566 service.ensureViewerLoaded(context).then(function () {
76567 service.selectImage(context, photoKey).showViewer(context);
76574 context.layers().on('change.rendererPhotos', updateStorage);
76577 return utilRebind(photos, dispatch$1, 'on');
76580 function uiAccount(context) {
76581 var osm = context.connection();
76583 function update(selection) {
76586 if (!osm.authenticated()) {
76587 selection.selectAll('.userLink, .logoutLink').classed('hide', true);
76591 osm.userDetails(function (err, details) {
76592 var userLink = selection.select('.userLink'),
76593 logoutLink = selection.select('.logoutLink');
76595 logoutLink.html('');
76596 if (err || !details) return;
76597 selection.selectAll('.userLink, .logoutLink').classed('hide', false); // Link
76599 var userLinkA = userLink.append('a').attr('href', osm.userURL(details.display_name)).attr('target', '_blank'); // Add thumbnail or dont
76601 if (details.image_url) {
76602 userLinkA.append('img').attr('class', 'icon pre-text user-icon').attr('src', details.image_url);
76604 userLinkA.call(svgIcon('#iD-icon-avatar', 'pre-text light'));
76608 userLinkA.append('span').attr('class', 'label').html(details.display_name);
76609 logoutLink.append('a').attr('class', 'logout').attr('href', '#').html(_t.html('logout')).on('click.logout', function (d3_event) {
76610 d3_event.preventDefault();
76616 return function (selection) {
76617 selection.append('li').attr('class', 'userLink').classed('hide', true);
76618 selection.append('li').attr('class', 'logoutLink').classed('hide', true);
76621 osm.on('change.account', function () {
76629 function uiAttribution(context) {
76630 var _selection = select(null);
76632 function render(selection, data, klass) {
76633 var div = selection.selectAll(".".concat(klass)).data([0]);
76634 div = div.enter().append('div').attr('class', klass).merge(div);
76635 var attributions = div.selectAll('.attribution').data(data, function (d) {
76638 attributions.exit().remove();
76639 attributions = attributions.enter().append('span').attr('class', 'attribution').each(function (d, i, nodes) {
76640 var attribution = select(nodes[i]);
76642 if (d.terms_html) {
76643 attribution.html(d.terms_html);
76648 attribution = attribution.append('a').attr('href', d.terms_url).attr('target', '_blank');
76651 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
76652 var terms_text = _t("imagery.".concat(sourceID, ".attribution.text"), {
76653 "default": d.terms_text || d.id || d.name()
76656 if (d.icon && !d.overlay) {
76657 attribution.append('img').attr('class', 'source-image').attr('src', d.icon);
76660 attribution.append('span').attr('class', 'attribution-text').html(terms_text);
76661 }).merge(attributions);
76662 var copyright = attributions.selectAll('.copyright-notice').data(function (d) {
76663 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
76664 return notice ? [notice] : [];
76666 copyright.exit().remove();
76667 copyright = copyright.enter().append('span').attr('class', 'copyright-notice').merge(copyright);
76668 copyright.html(String);
76671 function update() {
76672 var baselayer = context.background().baseLayerSource();
76674 _selection.call(render, baselayer ? [baselayer] : [], 'base-layer-attribution');
76676 var z = context.map().zoom();
76677 var overlays = context.background().overlayLayerSources() || [];
76679 _selection.call(render, overlays.filter(function (s) {
76680 return s.validZoom(z);
76681 }), 'overlay-layer-attribution');
76684 return function (selection) {
76685 _selection = selection;
76686 context.background().on('change.attribution', update);
76687 context.map().on('move.attribution', throttle(update, 400, {
76694 function uiContributors(context) {
76695 var osm = context.connection(),
76696 debouncedUpdate = debounce(function () {
76701 wrap = select(null);
76703 function update() {
76706 entities = context.history().intersects(context.map().extent());
76707 entities.forEach(function (entity) {
76708 if (entity && entity.user) users[entity.user] = true;
76710 var u = Object.keys(users),
76711 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
76712 wrap.html('').call(svgIcon('#iD-icon-nearby', 'pre-text light'));
76713 var userList = select(document.createElement('span'));
76714 userList.selectAll().data(subset).enter().append('a').attr('class', 'user-link').attr('href', function (d) {
76715 return osm.userURL(d);
76716 }).attr('target', '_blank').html(String);
76718 if (u.length > limit) {
76719 var count = select(document.createElement('span'));
76720 var othersNum = u.length - limit + 1;
76721 count.append('a').attr('target', '_blank').attr('href', function () {
76722 return osm.changesetsURL(context.map().center(), context.map().zoom());
76723 }).html(othersNum);
76724 wrap.append('span').html(_t.html('contributors.truncated_list', {
76726 users: userList.html(),
76727 count: count.html()
76730 wrap.append('span').html(_t.html('contributors.list', {
76731 users: userList.html()
76737 wrap.transition().style('opacity', 0);
76738 } else if (hidden) {
76739 wrap.transition().style('opacity', 1);
76743 return function (selection) {
76747 osm.on('loaded.contributors', debouncedUpdate);
76748 context.map().on('move.contributors', debouncedUpdate);
76752 var _popoverID = 0;
76753 function uiPopover(klass) {
76754 var _id = _popoverID++;
76756 var _anchorSelection = select(null);
76758 var popover = function popover(selection) {
76759 _anchorSelection = selection;
76760 selection.each(setup);
76763 var _animation = utilFunctor(false);
76765 var _placement = utilFunctor('top'); // top, bottom, left, right
76768 var _alignment = utilFunctor('center'); // leading, center, trailing
76771 var _scrollContainer = utilFunctor(select(null));
76775 var _displayType = utilFunctor('');
76777 var _hasArrow = utilFunctor(true); // use pointer events on supported platforms; fallback to mouse events
76780 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
76782 popover.displayType = function (val) {
76783 if (arguments.length) {
76784 _displayType = utilFunctor(val);
76787 return _displayType;
76791 popover.hasArrow = function (val) {
76792 if (arguments.length) {
76793 _hasArrow = utilFunctor(val);
76800 popover.placement = function (val) {
76801 if (arguments.length) {
76802 _placement = utilFunctor(val);
76809 popover.alignment = function (val) {
76810 if (arguments.length) {
76811 _alignment = utilFunctor(val);
76818 popover.scrollContainer = function (val) {
76819 if (arguments.length) {
76820 _scrollContainer = utilFunctor(val);
76823 return _scrollContainer;
76827 popover.content = function (val) {
76828 if (arguments.length) {
76836 popover.isShown = function () {
76837 var popoverSelection = _anchorSelection.select('.popover-' + _id);
76839 return !popoverSelection.empty() && popoverSelection.classed('in');
76842 popover.show = function () {
76843 _anchorSelection.each(show);
76846 popover.updateContent = function () {
76847 _anchorSelection.each(updateContent);
76850 popover.hide = function () {
76851 _anchorSelection.each(hide);
76854 popover.toggle = function () {
76855 _anchorSelection.each(toggle);
76858 popover.destroy = function (selection, selector) {
76859 // by default, just destroy the current popover
76860 selector = selector || '.popover-' + _id;
76861 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 () {
76862 return this.getAttribute('data-original-title') || this.getAttribute('title');
76863 }).attr('data-original-title', null).selectAll(selector).remove();
76866 popover.destroyAny = function (selection) {
76867 selection.call(popover.destroy, '.popover');
76871 var anchor = select(this);
76873 var animate = _animation.apply(this, arguments);
76875 var popoverSelection = anchor.selectAll('.popover-' + _id).data([0]);
76876 var enter = popoverSelection.enter().append('div').attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')).classed('arrowed', _hasArrow.apply(this, arguments));
76877 enter.append('div').attr('class', 'popover-arrow');
76878 enter.append('div').attr('class', 'popover-inner');
76879 popoverSelection = enter.merge(popoverSelection);
76882 popoverSelection.classed('fade', true);
76885 var display = _displayType.apply(this, arguments);
76887 if (display === 'hover') {
76888 var _lastNonMouseEnterTime;
76890 anchor.on(_pointerPrefix + 'enter.popover', function (d3_event) {
76891 if (d3_event.pointerType) {
76892 if (d3_event.pointerType !== 'mouse') {
76893 _lastNonMouseEnterTime = d3_event.timeStamp; // only allow hover behavior for mouse input
76896 } else if (_lastNonMouseEnterTime && d3_event.timeStamp - _lastNonMouseEnterTime < 1500) {
76897 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
76898 // event for non-mouse interactions right after sending
76899 // the correct type pointerenter event. Workaround by discarding
76900 // any mouse event that occurs immediately after a non-mouse event.
76903 } // don't show if buttons are pressed, e.g. during click and drag of map
76906 if (d3_event.buttons !== 0) return;
76907 show.apply(this, arguments);
76908 }).on(_pointerPrefix + 'leave.popover', function () {
76909 hide.apply(this, arguments);
76910 }) // show on focus too for better keyboard navigation support
76911 .on('focus.popover', function () {
76912 show.apply(this, arguments);
76913 }).on('blur.popover', function () {
76914 hide.apply(this, arguments);
76916 } else if (display === 'clickFocus') {
76917 anchor.on(_pointerPrefix + 'down.popover', function (d3_event) {
76918 d3_event.preventDefault();
76919 d3_event.stopPropagation();
76920 }).on(_pointerPrefix + 'up.popover', function (d3_event) {
76921 d3_event.preventDefault();
76922 d3_event.stopPropagation();
76923 }).on('click.popover', toggle);
76924 popoverSelection // This attribute lets the popover take focus
76925 .attr('tabindex', 0).on('blur.popover', function () {
76926 anchor.each(function () {
76927 hide.apply(this, arguments);
76934 var anchor = select(this);
76935 var popoverSelection = anchor.selectAll('.popover-' + _id);
76937 if (popoverSelection.empty()) {
76938 // popover was removed somehow, put it back
76939 anchor.call(popover.destroy);
76940 anchor.each(setup);
76941 popoverSelection = anchor.selectAll('.popover-' + _id);
76944 popoverSelection.classed('in', true);
76946 var displayType = _displayType.apply(this, arguments);
76948 if (displayType === 'clickFocus') {
76949 anchor.classed('active', true);
76950 popoverSelection.node().focus();
76953 anchor.each(updateContent);
76956 function updateContent() {
76957 var anchor = select(this);
76960 anchor.selectAll('.popover-' + _id + ' > .popover-inner').call(_content.apply(this, arguments));
76963 updatePosition.apply(this, arguments); // hack: update multiple times to fix instances where the absolute offset is
76964 // set before the dynamic popover size is calculated by the browser
76966 updatePosition.apply(this, arguments);
76967 updatePosition.apply(this, arguments);
76970 function updatePosition() {
76971 var anchor = select(this);
76972 var popoverSelection = anchor.selectAll('.popover-' + _id);
76974 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
76976 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
76977 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
76978 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
76980 var placement = _placement.apply(this, arguments);
76982 popoverSelection.classed('left', false).classed('right', false).classed('top', false).classed('bottom', false).classed(placement, true);
76984 var alignment = _alignment.apply(this, arguments);
76986 var alignFactor = 0.5;
76988 if (alignment === 'leading') {
76990 } else if (alignment === 'trailing') {
76994 var anchorFrame = getFrame(anchor.node());
76995 var popoverFrame = getFrame(popoverSelection.node());
76998 switch (placement) {
77001 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
77002 y: anchorFrame.y - popoverFrame.h
77008 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
77009 y: anchorFrame.y + anchorFrame.h
77015 x: anchorFrame.x - popoverFrame.w,
77016 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
77022 x: anchorFrame.x + anchorFrame.w,
77023 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
77029 if (scrollNode && (placement === 'top' || placement === 'bottom')) {
77030 var initialPosX = position.x;
77032 if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
77033 position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
77034 } else if (position.x < 10) {
77038 var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); // keep the arrow centered on the button, or as close as possible
77040 var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
77041 arrow.style('left', ~~arrowPosX + 'px');
77044 popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
77046 popoverSelection.style('left', null).style('top', null);
77049 function getFrame(node) {
77050 var positionStyle = select(node).style('position');
77052 if (positionStyle === 'absolute' || positionStyle === 'static') {
77054 x: node.offsetLeft - scrollLeft,
77055 y: node.offsetTop - scrollTop,
77056 w: node.offsetWidth,
77057 h: node.offsetHeight
77063 w: node.offsetWidth,
77064 h: node.offsetHeight
77071 var anchor = select(this);
77073 if (_displayType.apply(this, arguments) === 'clickFocus') {
77074 anchor.classed('active', false);
77077 anchor.selectAll('.popover-' + _id).classed('in', false);
77080 function toggle() {
77081 if (select(this).select('.popover-' + _id).classed('in')) {
77082 hide.apply(this, arguments);
77084 show.apply(this, arguments);
77091 function uiTooltip(klass) {
77092 var tooltip = uiPopover((klass || '') + ' tooltip').displayType('hover');
77094 var _title = function _title() {
77095 var title = this.getAttribute('data-original-title');
77100 title = this.getAttribute('title');
77101 this.removeAttribute('title');
77102 this.setAttribute('data-original-title', title);
77108 var _heading = utilFunctor(null);
77110 var _keys = utilFunctor(null);
77112 tooltip.title = function (val) {
77113 if (!arguments.length) return _title;
77114 _title = utilFunctor(val);
77118 tooltip.heading = function (val) {
77119 if (!arguments.length) return _heading;
77120 _heading = utilFunctor(val);
77124 tooltip.keys = function (val) {
77125 if (!arguments.length) return _keys;
77126 _keys = utilFunctor(val);
77130 tooltip.content(function () {
77131 var heading = _heading.apply(this, arguments);
77133 var text = _title.apply(this, arguments);
77135 var keys = _keys.apply(this, arguments);
77137 return function (selection) {
77138 var headingSelect = selection.selectAll('.tooltip-heading').data(heading ? [heading] : []);
77139 headingSelect.exit().remove();
77140 headingSelect.enter().append('div').attr('class', 'tooltip-heading').merge(headingSelect).html(heading);
77141 var textSelect = selection.selectAll('.tooltip-text').data(text ? [text] : []);
77142 textSelect.exit().remove();
77143 textSelect.enter().append('div').attr('class', 'tooltip-text').merge(textSelect).html(text);
77144 var keyhintWrap = selection.selectAll('.keyhint-wrap').data(keys && keys.length ? [0] : []);
77145 keyhintWrap.exit().remove();
77146 var keyhintWrapEnter = keyhintWrap.enter().append('div').attr('class', 'keyhint-wrap');
77147 keyhintWrapEnter.append('span').html(_t.html('tooltip_keyhint'));
77148 keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
77149 keyhintWrap.selectAll('kbd.shortcut').data(keys && keys.length ? keys : []).enter().append('kbd').attr('class', 'shortcut').html(function (d) {
77157 function uiEditMenu(context) {
77158 var dispatch$1 = dispatch('toggled');
77160 var _menu = select(null);
77162 var _operations = []; // the position the menu should be displayed relative to
77164 var _anchorLoc = [0, 0];
77165 var _anchorLocLonLat = [0, 0]; // a string indicating how the menu was opened
77167 var _triggerType = '';
77168 var _vpTopMargin = 85; // viewport top margin
77170 var _vpBottomMargin = 45; // viewport bottom margin
77172 var _vpSideMargin = 35; // viewport side margin
77174 var _menuTop = false;
77178 var _menuWidth; // hardcode these values to make menu positioning easier
77181 var _verticalPadding = 4; // see also `.edit-menu .tooltip` CSS; include margin
77183 var _tooltipWidth = 210; // offset the menu slightly from the target location
77185 var _menuSideMargin = 10;
77186 var _tooltips = [];
77188 var editMenu = function editMenu(selection) {
77189 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
77191 var ops = _operations.filter(function (op) {
77192 return !isTouchMenu || !op.mouseOnly;
77195 if (!ops.length) return;
77196 _tooltips = []; // Position the menu above the anchor for stylus and finger input
77197 // since the mapper's hand likely obscures the screen below the anchor
77199 _menuTop = isTouchMenu; // Show labels for touch input since there aren't hover tooltips
77201 var showLabels = isTouchMenu;
77202 var buttonHeight = showLabels ? 32 : 34;
77205 // Get a general idea of the width based on the length of the label
77206 _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function (op) {
77207 return op.title.length;
77213 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
77214 _menu = selection.append('div').attr('class', 'edit-menu').classed('touch-menu', isTouchMenu).style('padding', _verticalPadding + 'px 0');
77216 var buttons = _menu.selectAll('.edit-menu-item').data(ops); // enter
77219 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
77220 return 'edit-menu-item edit-menu-item-' + d.id;
77221 }).style('height', buttonHeight + 'px').on('click', click) // don't listen for `mouseup` because we only care about non-mouse pointer types
77222 .on('pointerup', pointerup).on('pointerdown mousedown', function pointerdown(d3_event) {
77223 // don't let button presses also act as map input - #1869
77224 d3_event.stopPropagation();
77225 }).on('mouseenter.highlight', function (d3_event, d) {
77226 if (!d.relatedEntityIds || select(this).classed('disabled')) return;
77227 utilHighlightEntities(d.relatedEntityIds(), true, context);
77228 }).on('mouseleave.highlight', function (d3_event, d) {
77229 if (!d.relatedEntityIds) return;
77230 utilHighlightEntities(d.relatedEntityIds(), false, context);
77232 buttonsEnter.each(function (d) {
77233 var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]);
77235 _tooltips.push(tooltip);
77237 select(this).call(tooltip).append('div').attr('class', 'icon-wrap').call(svgIcon('#iD-operation-' + d.id, 'operation'));
77241 buttonsEnter.append('span').attr('class', 'label').html(function (d) {
77247 buttonsEnter.merge(buttons).classed('disabled', function (d) {
77248 return d.disabled();
77251 var initialScale = context.projection.scale();
77252 context.map().on('move.edit-menu', function () {
77253 if (initialScale !== context.projection.scale()) {
77256 }).on('drawn.edit-menu', function (info) {
77257 if (info.full) updatePosition();
77259 var lastPointerUpType; // `pointerup` is always called before `click`
77261 function pointerup(d3_event) {
77262 lastPointerUpType = d3_event.pointerType;
77265 function click(d3_event, operation) {
77266 d3_event.stopPropagation();
77268 if (operation.relatedEntityIds) {
77269 utilHighlightEntities(operation.relatedEntityIds(), false, context);
77272 if (operation.disabled()) {
77273 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
77274 // there are no tooltips for touch interactions so flash feedback instead
77275 context.ui().flash.duration(4000).iconName('#iD-operation-' + operation.id).iconClass('operation disabled').label(operation.tooltip)();
77278 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
77279 context.ui().flash.duration(2000).iconName('#iD-operation-' + operation.id).iconClass('operation').label(operation.annotation() || operation.title)();
77286 lastPointerUpType = null;
77289 dispatch$1.call('toggled', this, true);
77292 function updatePosition() {
77293 if (!_menu || _menu.empty()) return;
77294 var anchorLoc = context.projection(_anchorLocLonLat);
77295 var viewport = context.surfaceRect();
77297 if (anchorLoc[0] < 0 || anchorLoc[0] > viewport.width || anchorLoc[1] < 0 || anchorLoc[1] > viewport.height) {
77298 // close the menu if it's gone offscreen
77303 var menuLeft = displayOnLeft(viewport);
77304 var offset = [0, 0];
77305 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
77308 if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
77309 // menu is near top viewport edge, shift downward
77310 offset[1] = -anchorLoc[1] + _vpTopMargin;
77312 offset[1] = -_menuHeight;
77315 if (anchorLoc[1] + _menuHeight > viewport.height - _vpBottomMargin) {
77316 // menu is near bottom viewport edge, shift upwards
77317 offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
77323 var origin = geoVecAdd(anchorLoc, offset);
77325 _menu.style('left', origin[0] + 'px').style('top', origin[1] + 'px');
77327 var tooltipSide = tooltipPosition(viewport, menuLeft);
77329 _tooltips.forEach(function (tooltip) {
77330 tooltip.placement(tooltipSide);
77333 function displayOnLeft(viewport) {
77334 if (_mainLocalizer.textDirection() === 'ltr') {
77335 if (anchorLoc[0] + _menuSideMargin + _menuWidth > viewport.width - _vpSideMargin) {
77336 // right menu would be too close to the right viewport edge, go left
77338 } // prefer right menu
77344 if (anchorLoc[0] - _menuSideMargin - _menuWidth < _vpSideMargin) {
77345 // left menu would be too close to the left viewport edge, go right
77347 } // prefer left menu
77354 function tooltipPosition(viewport, menuLeft) {
77355 if (_mainLocalizer.textDirection() === 'ltr') {
77357 // if there's not room for a right-side menu then there definitely
77358 // isn't room for right-side tooltips
77362 if (anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth > viewport.width - _vpSideMargin) {
77363 // right tooltips would be too close to the right viewport edge, go left
77365 } // prefer right tooltips
77375 if (anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth < _vpSideMargin) {
77376 // left tooltips would be too close to the left viewport edge, go right
77378 } // prefer left tooltips
77386 editMenu.close = function () {
77387 context.map().on('move.edit-menu', null).on('drawn.edit-menu', null);
77392 dispatch$1.call('toggled', this, false);
77395 editMenu.anchorLoc = function (val) {
77396 if (!arguments.length) return _anchorLoc;
77398 _anchorLocLonLat = context.projection.invert(_anchorLoc);
77402 editMenu.triggerType = function (val) {
77403 if (!arguments.length) return _triggerType;
77404 _triggerType = val;
77408 editMenu.operations = function (val) {
77409 if (!arguments.length) return _operations;
77414 return utilRebind(editMenu, dispatch$1, 'on');
77417 function uiFeatureInfo(context) {
77418 function update(selection) {
77419 var features = context.features();
77420 var stats = features.stats();
77422 var hiddenList = features.hidden().map(function (k) {
77425 return _t('inspector.title_count', {
77426 title: _t.html('feature.' + k + '.description'),
77432 }).filter(Boolean);
77433 selection.html('');
77435 if (hiddenList.length) {
77436 var tooltipBehavior = uiTooltip().placement('top').title(function () {
77437 return hiddenList.join('<br/>');
77439 selection.append('a').attr('class', 'chip').attr('href', '#').html(_t.html('feature_info.hidden_warning', {
77441 })).call(tooltipBehavior).on('click', function (d3_event) {
77442 tooltipBehavior.hide();
77443 d3_event.preventDefault(); // open the Map Data pane
77445 context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
77449 selection.classed('hide', !hiddenList.length);
77452 return function (selection) {
77454 context.features().on('change.feature_info', function () {
77460 function uiFlash(context) {
77463 var _duration = 2000;
77464 var _iconName = '#iD-icon-no';
77465 var _iconClass = 'disabled';
77470 _flashTimer.stop();
77473 context.container().select('.main-footer-wrap').classed('footer-hide', true).classed('footer-show', false);
77474 context.container().select('.flash-wrap').classed('footer-hide', false).classed('footer-show', true);
77475 var content = context.container().select('.flash-wrap').selectAll('.flash-content').data([0]); // Enter
77477 var contentEnter = content.enter().append('div').attr('class', 'flash-content');
77478 var iconEnter = contentEnter.append('svg').attr('class', 'flash-icon icon').append('g').attr('transform', 'translate(10,10)');
77479 iconEnter.append('circle').attr('r', 9);
77480 iconEnter.append('use').attr('transform', 'translate(-7,-7)').attr('width', '14').attr('height', '14');
77481 contentEnter.append('div').attr('class', 'flash-text'); // Update
77483 content = content.merge(contentEnter);
77484 content.selectAll('.flash-icon').attr('class', 'icon flash-icon ' + (_iconClass || ''));
77485 content.selectAll('.flash-icon use').attr('xlink:href', _iconName);
77486 content.selectAll('.flash-text').attr('class', 'flash-text').html(_label);
77487 _flashTimer = d3_timeout(function () {
77488 _flashTimer = null;
77489 context.container().select('.main-footer-wrap').classed('footer-hide', false).classed('footer-show', true);
77490 context.container().select('.flash-wrap').classed('footer-hide', true).classed('footer-show', false);
77495 flash.duration = function (_) {
77496 if (!arguments.length) return _duration;
77501 flash.label = function (_) {
77502 if (!arguments.length) return _label;
77507 flash.iconName = function (_) {
77508 if (!arguments.length) return _iconName;
77513 flash.iconClass = function (_) {
77514 if (!arguments.length) return _iconClass;
77522 function uiFullScreen(context) {
77523 var element = context.container().node(); // var button = d3_select(null);
77525 function getFullScreenFn() {
77526 if (element.requestFullscreen) {
77527 return element.requestFullscreen;
77528 } else if (element.msRequestFullscreen) {
77529 return element.msRequestFullscreen;
77530 } else if (element.mozRequestFullScreen) {
77531 return element.mozRequestFullScreen;
77532 } else if (element.webkitRequestFullscreen) {
77533 return element.webkitRequestFullscreen;
77537 function getExitFullScreenFn() {
77538 if (document.exitFullscreen) {
77539 return document.exitFullscreen;
77540 } else if (document.msExitFullscreen) {
77541 return document.msExitFullscreen;
77542 } else if (document.mozCancelFullScreen) {
77543 return document.mozCancelFullScreen;
77544 } else if (document.webkitExitFullscreen) {
77545 return document.webkitExitFullscreen;
77549 function isFullScreen() {
77550 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
77553 function isSupported() {
77554 return !!getFullScreenFn();
77557 function fullScreen(d3_event) {
77558 d3_event.preventDefault();
77560 if (!isFullScreen()) {
77561 // button.classed('active', true);
77562 getFullScreenFn().apply(element);
77564 // button.classed('active', false);
77565 getExitFullScreenFn().apply(document);
77569 return function () {
77571 if (!isSupported()) return; // button = selection.append('button')
77572 // .attr('title', t('full_screen'))
77573 // .on('click', fullScreen)
77575 // button.append('span')
77576 // .attr('class', 'icon full-screen');
77578 var detected = utilDetect();
77579 var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11'];
77580 context.keybinding().on(keys, fullScreen);
77584 function uiGeolocate(context) {
77585 var _geolocationOptions = {
77586 // prioritize speed and power usage over precision
77587 enableHighAccuracy: false,
77588 // don't hang indefinitely getting the location
77589 timeout: 6000 // 6sec
77593 var _locating = uiLoading(context).message(_t.html('geolocate.locating')).blocking(true);
77595 var _layer = context.layers().layer('geolocate');
77603 var _button = select(null);
77606 if (context.inIntro()) return;
77608 if (!_layer.enabled() && !_locating.isShown()) {
77609 // This timeout ensures that we still call finish() even if
77610 // the user declines to share their location in Firefox
77611 _timeoutID = setTimeout(error, 10000
77614 context.container().call(_locating); // get the latest position even if we already have one
77616 navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
77620 _layer.enabled(null, false);
77622 updateButtonState();
77626 function zoomTo() {
77627 context.enter(modeBrowse(context));
77628 var map = context.map();
77630 _layer.enabled(_position, true);
77632 updateButtonState();
77633 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
77636 function success(geolocation) {
77637 _position = geolocation;
77638 var coords = _position.coords;
77639 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
77646 // use the position from a previous call if we have one
77649 context.ui().flash.label(_t.html('geolocate.location_unavailable')).iconName('#iD-icon-geolocate')();
77655 function finish() {
77656 _locating.close(); // unblock ui
77660 clearTimeout(_timeoutID);
77663 _timeoutID = undefined;
77666 function updateButtonState() {
77667 _button.classed('active', _layer.enabled());
77670 return function (selection) {
77671 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
77672 _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')]));
77673 context.keybinding().on(_t('geolocate.key'), click);
77677 function uiPanelBackground(context) {
77678 var background = context.background();
77679 var _currSourceName = null;
77680 var _metadata = {};
77681 var _metadataKeys = ['zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'];
77683 var debouncedRedraw = debounce(redraw, 250);
77685 function redraw(selection) {
77686 var source = background.baseLayerSource();
77687 if (!source) return;
77688 var isDG = source.id.match(/^DigitalGlobe/i) !== null;
77689 var sourceLabel = source.label();
77691 if (_currSourceName !== sourceLabel) {
77692 _currSourceName = sourceLabel;
77696 selection.html('');
77697 var list = selection.append('ul').attr('class', 'background-info');
77698 list.append('li').html(_currSourceName);
77700 _metadataKeys.forEach(function (k) {
77701 // DigitalGlobe vintage is available in raster layers for now.
77702 if (isDG && k === 'vintage') return;
77703 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]);
77706 debouncedGetMetadata(selection);
77707 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
77708 selection.append('a').html(_t.html('info_panels.background.' + toggleTiles)).attr('href', '#').attr('class', 'button button-toggle-tiles').on('click', function (d3_event) {
77709 d3_event.preventDefault();
77710 context.setDebug('tile', !context.getDebug('tile'));
77711 selection.call(redraw);
77715 var key = source.id + '-vintage';
77716 var sourceVintage = context.background().findSource(key);
77717 var showsVintage = context.background().showsLayer(sourceVintage);
77718 var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
77719 selection.append('a').html(_t.html('info_panels.background.' + toggleVintage)).attr('href', '#').attr('class', 'button button-toggle-vintage').on('click', function (d3_event) {
77720 d3_event.preventDefault();
77721 context.background().toggleOverlayLayer(sourceVintage);
77722 selection.call(redraw);
77724 } // disable if necessary
77727 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function (layerId) {
77728 if (source.id !== layerId) {
77729 var key = layerId + '-vintage';
77730 var sourceVintage = context.background().findSource(key);
77732 if (context.background().showsLayer(sourceVintage)) {
77733 context.background().toggleOverlayLayer(sourceVintage);
77739 var debouncedGetMetadata = debounce(getMetadata, 250);
77741 function getMetadata(selection) {
77742 var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center
77744 if (tile.empty()) return;
77745 var sourceName = _currSourceName;
77746 var d = tile.datum();
77747 var zoom = d && d.length >= 3 && d[2] || Math.floor(context.map().zoom());
77748 var center = context.map().center(); // update zoom
77750 _metadata.zoom = String(zoom);
77751 selection.selectAll('.background-info-list-zoom').classed('hide', false).selectAll('.background-info-span-zoom').html(_metadata.zoom);
77752 if (!d || !d.length >= 3) return;
77753 background.baseLayerSource().getMetadata(center, d, function (err, result) {
77754 if (err || _currSourceName !== sourceName) return; // update vintage
77756 var vintage = result.vintage;
77757 _metadata.vintage = vintage && vintage.range || _t('info_panels.background.unknown');
77758 selection.selectAll('.background-info-list-vintage').classed('hide', false).selectAll('.background-info-span-vintage').html(_metadata.vintage); // update other _metadata
77760 _metadataKeys.forEach(function (k) {
77761 if (k === 'zoom' || k === 'vintage') return; // done already
77763 var val = result[k];
77764 _metadata[k] = val;
77765 selection.selectAll('.background-info-list-' + k).classed('hide', !val).selectAll('.background-info-span-' + k).html(val);
77770 var panel = function panel(selection) {
77771 selection.call(redraw);
77772 context.map().on('drawn.info-background', function () {
77773 selection.call(debouncedRedraw);
77774 }).on('move.info-background', function () {
77775 selection.call(debouncedGetMetadata);
77779 panel.off = function () {
77780 context.map().on('drawn.info-background', null).on('move.info-background', null);
77783 panel.id = 'background';
77784 panel.label = _t.html('info_panels.background.title');
77785 panel.key = _t('info_panels.background.key');
77789 function uiPanelHistory(context) {
77792 function displayTimestamp(timestamp) {
77793 if (!timestamp) return _t('info_panels.history.unknown');
77802 var d = new Date(timestamp);
77803 if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
77804 return d.toLocaleString(_mainLocalizer.localeCode(), options);
77807 function displayUser(selection, userName) {
77809 selection.append('span').html(_t.html('info_panels.history.unknown'));
77813 selection.append('span').attr('class', 'user-name').html(userName);
77814 var links = selection.append('div').attr('class', 'links');
77817 links.append('a').attr('class', 'user-osm-link').attr('href', osm.userURL(userName)).attr('target', '_blank').html('OSM');
77820 links.append('a').attr('class', 'user-hdyc-link').attr('href', 'https://hdyc.neis-one.org/?' + userName).attr('target', '_blank').attr('tabindex', -1).html('HDYC');
77823 function displayChangeset(selection, changeset) {
77825 selection.append('span').html(_t.html('info_panels.history.unknown'));
77829 selection.append('span').attr('class', 'changeset-id').html(changeset);
77830 var links = selection.append('div').attr('class', 'links');
77833 links.append('a').attr('class', 'changeset-osm-link').attr('href', osm.changesetURL(changeset)).attr('target', '_blank').html('OSM');
77836 links.append('a').attr('class', 'changeset-osmcha-link').attr('href', 'https://osmcha.org/changesets/' + changeset).attr('target', '_blank').html('OSMCha');
77837 links.append('a').attr('class', 'changeset-achavi-link').attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset).attr('target', '_blank').html('Achavi');
77840 function redraw(selection) {
77841 var selectedNoteID = context.selectedNoteID();
77842 osm = context.connection();
77843 var selected, note, entity;
77845 if (selectedNoteID && osm) {
77847 selected = [_t('note.note') + ' ' + selectedNoteID];
77848 note = osm.getNote(selectedNoteID);
77850 // selected 1..n entities
77851 selected = context.selectedIDs().filter(function (e) {
77852 return context.hasEntity(e);
77855 if (selected.length) {
77856 entity = context.entity(selected[0]);
77860 var singular = selected.length === 1 ? selected[0] : null;
77861 selection.html('');
77862 selection.append('h4').attr('class', 'history-heading').html(singular || _t.html('info_panels.selected', {
77865 if (!singular) return;
77868 selection.call(redrawEntity, entity);
77870 selection.call(redrawNote, note);
77874 function redrawNote(selection, note) {
77875 if (!note || note.isNew()) {
77876 selection.append('div').html(_t.html('info_panels.history.note_no_history'));
77880 var list = selection.append('ul');
77881 list.append('li').html(_t.html('info_panels.history.note_comments') + ':').append('span').html(note.comments.length);
77883 if (note.comments.length) {
77884 list.append('li').html(_t.html('info_panels.history.note_created_date') + ':').append('span').html(displayTimestamp(note.comments[0].date));
77885 list.append('li').html(_t.html('info_panels.history.note_created_user') + ':').call(displayUser, note.comments[0].user);
77889 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'));
77893 function redrawEntity(selection, entity) {
77894 if (!entity || entity.isNew()) {
77895 selection.append('div').html(_t.html('info_panels.history.no_history'));
77899 var links = selection.append('div').attr('class', 'links');
77902 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');
77905 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');
77906 var list = selection.append('ul');
77907 list.append('li').html(_t.html('info_panels.history.version') + ':').append('span').html(entity.version);
77908 list.append('li').html(_t.html('info_panels.history.last_edit') + ':').append('span').html(displayTimestamp(entity.timestamp));
77909 list.append('li').html(_t.html('info_panels.history.edited_by') + ':').call(displayUser, entity.user);
77910 list.append('li').html(_t.html('info_panels.history.changeset') + ':').call(displayChangeset, entity.changeset);
77913 var panel = function panel(selection) {
77914 selection.call(redraw);
77915 context.map().on('drawn.info-history', function () {
77916 selection.call(redraw);
77918 context.on('enter.info-history', function () {
77919 selection.call(redraw);
77923 panel.off = function () {
77924 context.map().on('drawn.info-history', null);
77925 context.on('enter.info-history', null);
77928 panel.id = 'history';
77929 panel.label = _t.html('info_panels.history.title');
77930 panel.key = _t('info_panels.history.key');
77934 var OSM_PRECISION = 7;
77936 * Returns a localized representation of the given length measurement.
77938 * @param {Number} m area in meters
77939 * @param {Boolean} isImperial true for U.S. customary units; false for metric
77942 function displayLength(m, isImperial) {
77943 var d = m * (isImperial ? 3.28084 : 1);
77956 unit = 'kilometers';
77962 return _t('units.' + unit, {
77963 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
77964 maximumSignificantDigits: 4
77969 * Returns a localized representation of the given area measurement.
77971 * @param {Number} m2 area in square meters
77972 * @param {Boolean} isImperial true for U.S. customary units; false for metric
77975 function displayArea(m2, isImperial) {
77976 var locale = _mainLocalizer.localeCode();
77977 var d = m2 * (isImperial ? 10.7639111056 : 1);
77983 if (d >= 6969600) {
77984 // > 0.25mi² show mi²
77986 unit1 = 'square_miles';
77989 unit1 = 'square_feet';
77992 if (d > 4356 && d < 43560000) {
77993 // 0.1 - 1000 acres
77999 // > 0.25km² show km²
78001 unit1 = 'square_kilometers';
78004 unit1 = 'square_meters';
78007 if (d > 1000 && d < 10000000) {
78008 // 0.1 - 1000 hectares
78010 unit2 = 'hectares';
78014 area = _t('units.' + unit1, {
78015 quantity: d1.toLocaleString(locale, {
78016 maximumSignificantDigits: 4
78021 return _t('units.area_pair', {
78023 area2: _t('units.' + unit2, {
78024 quantity: d2.toLocaleString(locale, {
78025 maximumSignificantDigits: 2
78034 function wrap$2(x, min, max) {
78036 return ((x - min) % d + d) % d + min;
78039 function clamp$1(x, min, max) {
78040 return Math.max(min, Math.min(x, max));
78043 function displayCoordinate(deg, pos, neg) {
78044 var locale = _mainLocalizer.localeCode();
78045 var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
78046 var sec = (min - Math.floor(min)) * 60;
78047 var displayDegrees = _t('units.arcdegrees', {
78048 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
78050 var displayCoordinate;
78052 if (Math.floor(sec) > 0) {
78053 displayCoordinate = displayDegrees + _t('units.arcminutes', {
78054 quantity: Math.floor(min).toLocaleString(locale)
78055 }) + _t('units.arcseconds', {
78056 quantity: Math.round(sec).toLocaleString(locale)
78058 } else if (Math.floor(min) > 0) {
78059 displayCoordinate = displayDegrees + _t('units.arcminutes', {
78060 quantity: Math.round(min).toLocaleString(locale)
78063 displayCoordinate = _t('units.arcdegrees', {
78064 quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
78069 return displayCoordinate;
78071 return _t('units.coordinate', {
78072 coordinate: displayCoordinate,
78073 direction: _t('units.' + (deg > 0 ? pos : neg))
78078 * Returns given coordinate pair in degree-minute-second format.
78080 * @param {Array<Number>} coord longitude and latitude
78084 function dmsCoordinatePair(coord) {
78085 return _t('units.coordinate_pair', {
78086 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
78087 longitude: displayCoordinate(wrap$2(coord[0], -180, 180), 'east', 'west')
78091 * Returns the given coordinate pair in decimal format.
78092 * note: unlocalized to avoid comma ambiguity - see #4765
78094 * @param {Array<Number>} coord longitude and latitude
78097 function decimalCoordinatePair(coord) {
78098 return _t('units.coordinate_pair', {
78099 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
78100 longitude: wrap$2(coord[0], -180, 180).toFixed(OSM_PRECISION)
78104 function uiPanelLocation(context) {
78105 var currLocation = '';
78107 function redraw(selection) {
78108 selection.html('');
78109 var list = selection.append('ul'); // Mouse coordinates
78111 var coord = context.map().mouseCoordinates();
78113 if (coord.some(isNaN)) {
78114 coord = context.map().center();
78117 list.append('li').html(dmsCoordinatePair(coord)).append('li').html(decimalCoordinatePair(coord)); // Location Info
78119 selection.append('div').attr('class', 'location-info').html(currLocation || ' ');
78120 debouncedGetLocation(selection, coord);
78123 var debouncedGetLocation = debounce(getLocation, 250);
78125 function getLocation(selection, coord) {
78126 if (!services.geocoder) {
78127 currLocation = _t('info_panels.location.unknown_location');
78128 selection.selectAll('.location-info').html(currLocation);
78130 services.geocoder.reverse(coord, function (err, result) {
78131 currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
78132 selection.selectAll('.location-info').html(currLocation);
78137 var panel = function panel(selection) {
78138 selection.call(redraw);
78139 context.surface().on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function () {
78140 selection.call(redraw);
78144 panel.off = function () {
78145 context.surface().on('.info-location', null);
78148 panel.id = 'location';
78149 panel.label = _t.html('info_panels.location.title');
78150 panel.key = _t('info_panels.location.key');
78154 function uiPanelMeasurement(context) {
78155 function radiansToMeters(r) {
78156 // using WGS84 authalic radius (6371007.1809 m)
78157 return r * 6371007.1809;
78160 function steradiansToSqmeters(r) {
78161 // http://gis.stackexchange.com/a/124857/40446
78162 return r / (4 * Math.PI) * 510065621724000;
78165 function toLineString(feature) {
78166 if (feature.type === 'LineString') return feature;
78168 type: 'LineString',
78172 if (feature.type === 'Polygon') {
78173 result.coordinates = feature.coordinates[0];
78174 } else if (feature.type === 'MultiPolygon') {
78175 result.coordinates = feature.coordinates[0][0];
78181 function redraw(selection) {
78182 var graph = context.graph();
78183 var selectedNoteID = context.selectedNoteID();
78184 var osm = services.osm;
78185 var isImperial = !_mainLocalizer.usesMetric();
78186 var localeCode = _mainLocalizer.localeCode();
78188 var center, location, centroid;
78189 var closed, geometry;
78190 var totalNodeCount,
78195 if (selectedNoteID && osm) {
78197 var note = osm.getNote(selectedNoteID);
78198 heading = _t('note.note') + ' ' + selectedNoteID;
78199 location = note.loc;
78202 // selected 1..n entities
78203 var selectedIDs = context.selectedIDs().filter(function (id) {
78204 return context.hasEntity(id);
78206 var selected = selectedIDs.map(function (id) {
78207 return context.entity(id);
78209 heading = selected.length === 1 ? selected[0].id : _t('info_panels.selected', {
78213 if (selected.length) {
78214 var extent = geoExtent();
78216 for (var i in selected) {
78217 var entity = selected[i];
78219 extent._extend(entity.extent(graph));
78221 geometry = entity.geometry(graph);
78223 if (geometry === 'line' || geometry === 'area') {
78224 closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
78225 var feature = entity.asGeoJSON(graph);
78226 length += radiansToMeters(d3_geoLength(toLineString(feature)));
78227 centroid = d3_geoCentroid(feature);
78230 area += steradiansToSqmeters(entity.area(graph));
78235 if (selected.length > 1) {
78241 if (selected.length === 2 && selected[0].type === 'node' && selected[1].type === 'node') {
78242 distance = geoSphericalDistance(selected[0].loc, selected[1].loc);
78245 if (selected.length === 1 && selected[0].type === 'node') {
78246 location = selected[0].loc;
78248 totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
78251 if (!location && !centroid) {
78252 center = extent.center();
78257 selection.html('');
78260 selection.append('h4').attr('class', 'measurement-heading').html(heading);
78263 var list = selection.append('ul');
78267 list.append('li').html(_t.html('info_panels.measurement.geometry') + ':').append('span').html(closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry));
78270 if (totalNodeCount) {
78271 list.append('li').html(_t.html('info_panels.measurement.node_count') + ':').append('span').html(totalNodeCount.toLocaleString(localeCode));
78275 list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, isImperial));
78279 list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, isImperial));
78282 if (typeof distance === 'number') {
78283 list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, isImperial));
78287 coordItem = list.append('li').html(_t.html('info_panels.measurement.location') + ':');
78288 coordItem.append('span').html(dmsCoordinatePair(location));
78289 coordItem.append('span').html(decimalCoordinatePair(location));
78293 coordItem = list.append('li').html(_t.html('info_panels.measurement.centroid') + ':');
78294 coordItem.append('span').html(dmsCoordinatePair(centroid));
78295 coordItem.append('span').html(decimalCoordinatePair(centroid));
78299 coordItem = list.append('li').html(_t.html('info_panels.measurement.center') + ':');
78300 coordItem.append('span').html(dmsCoordinatePair(center));
78301 coordItem.append('span').html(decimalCoordinatePair(center));
78304 if (length || area || typeof distance === 'number') {
78305 var toggle = isImperial ? 'imperial' : 'metric';
78306 selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
78307 d3_event.preventDefault();
78308 isImperial = !isImperial;
78309 selection.call(redraw);
78314 var panel = function panel(selection) {
78315 selection.call(redraw);
78316 context.map().on('drawn.info-measurement', function () {
78317 selection.call(redraw);
78319 context.on('enter.info-measurement', function () {
78320 selection.call(redraw);
78324 panel.off = function () {
78325 context.map().on('drawn.info-measurement', null);
78326 context.on('enter.info-measurement', null);
78329 panel.id = 'measurement';
78330 panel.label = _t.html('info_panels.measurement.title');
78331 panel.key = _t('info_panels.measurement.key');
78335 var uiInfoPanels = {
78336 background: uiPanelBackground,
78337 history: uiPanelHistory,
78338 location: uiPanelLocation,
78339 measurement: uiPanelMeasurement
78342 function uiInfo(context) {
78343 var ids = Object.keys(uiInfoPanels);
78344 var wasActive = ['measurement'];
78346 var active = {}; // create panels
78348 ids.forEach(function (k) {
78350 panels[k] = uiInfoPanels[k](context);
78355 function info(selection) {
78356 function redraw() {
78357 var activeids = ids.filter(function (k) {
78360 var containers = infoPanels.selectAll('.panel-container').data(activeids, function (k) {
78363 containers.exit().style('opacity', 1).transition().duration(200).style('opacity', 0).on('end', function (d) {
78364 select(this).call(panels[d].off).remove();
78366 var enter = containers.enter().append('div').attr('class', function (d) {
78367 return 'fillD2 panel-container panel-container-' + d;
78369 enter.style('opacity', 0).transition().duration(200).style('opacity', 1);
78370 var title = enter.append('div').attr('class', 'panel-title fillD2');
78371 title.append('h3').html(function (d) {
78372 return panels[d].label;
78374 title.append('button').attr('class', 'close').on('click', function (d3_event, d) {
78375 d3_event.stopImmediatePropagation();
78376 d3_event.preventDefault();
78378 }).call(svgIcon('#iD-icon-close'));
78379 enter.append('div').attr('class', function (d) {
78380 return 'panel-content panel-content-' + d;
78381 }); // redraw the panels
78383 infoPanels.selectAll('.panel-content').each(function (d) {
78384 select(this).call(panels[d]);
78388 info.toggle = function (which) {
78389 var activeids = ids.filter(function (k) {
78395 active[which] = !active[which];
78397 if (activeids.length === 1 && activeids[0] === which) {
78398 // none active anymore
78399 wasActive = [which];
78402 context.container().select('.' + which + '-panel-toggle-item').classed('active', active[which]).select('input').property('checked', active[which]);
78405 if (activeids.length) {
78406 wasActive = activeids;
78407 activeids.forEach(function (k) {
78411 wasActive.forEach(function (k) {
78420 var infoPanels = selection.selectAll('.info-panels').data([0]);
78421 infoPanels = infoPanels.enter().append('div').attr('class', 'info-panels').merge(infoPanels);
78423 context.keybinding().on(uiCmd('⌘' + _t('info_panels.key')), function (d3_event) {
78424 d3_event.stopImmediatePropagation();
78425 d3_event.preventDefault();
78428 ids.forEach(function (k) {
78429 var key = _t('info_panels.' + k + '.key', {
78433 context.keybinding().on(uiCmd('⌘⇧' + key), function (d3_event) {
78434 d3_event.stopImmediatePropagation();
78435 d3_event.preventDefault();
78444 function pointBox(loc, context) {
78445 var rect = context.surfaceRect();
78446 var point = context.curtainProjection(loc);
78448 left: point[0] + rect.left - 40,
78449 top: point[1] + rect.top - 60,
78454 function pad(locOrBox, padding, context) {
78457 if (locOrBox instanceof Array) {
78458 var rect = context.surfaceRect();
78459 var point = context.curtainProjection(locOrBox);
78461 left: point[0] + rect.left,
78462 top: point[1] + rect.top
78469 left: box.left - padding,
78470 top: box.top - padding,
78471 width: (box.width || 0) + 2 * padding,
78472 height: (box.width || 0) + 2 * padding
78475 function icon(name, svgklass, useklass) {
78476 return '<svg class="icon ' + (svgklass || '') + '">' + '<use xlink:href="' + name + '"' + (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
78478 var helpStringReplacements; // Returns the localized HTML element for `id` with a standardized set of icon, key, and
78479 // label replacements suitable for tutorials and documentation. Optionally supplemented
78480 // with custom `replacements`
78482 function helpHtml(id, replacements) {
78483 // only load these the first time
78484 if (!helpStringReplacements) helpStringReplacements = {
78485 // insert icons corresponding to various UI elements
78486 point_icon: icon('#iD-icon-point', 'inline'),
78487 line_icon: icon('#iD-icon-line', 'inline'),
78488 area_icon: icon('#iD-icon-area', 'inline'),
78489 note_icon: icon('#iD-icon-note', 'inline add-note'),
78490 plus: icon('#iD-icon-plus', 'inline'),
78491 minus: icon('#iD-icon-minus', 'inline'),
78492 layers_icon: icon('#iD-icon-layers', 'inline'),
78493 data_icon: icon('#iD-icon-data', 'inline'),
78494 inspect: icon('#iD-icon-inspect', 'inline'),
78495 help_icon: icon('#iD-icon-help', 'inline'),
78496 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'inline'),
78497 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'inline'),
78498 save_icon: icon('#iD-icon-save', 'inline'),
78500 circularize_icon: icon('#iD-operation-circularize', 'inline operation'),
78501 continue_icon: icon('#iD-operation-continue', 'inline operation'),
78502 copy_icon: icon('#iD-operation-copy', 'inline operation'),
78503 delete_icon: icon('#iD-operation-delete', 'inline operation'),
78504 disconnect_icon: icon('#iD-operation-disconnect', 'inline operation'),
78505 downgrade_icon: icon('#iD-operation-downgrade', 'inline operation'),
78506 extract_icon: icon('#iD-operation-extract', 'inline operation'),
78507 merge_icon: icon('#iD-operation-merge', 'inline operation'),
78508 move_icon: icon('#iD-operation-move', 'inline operation'),
78509 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'inline operation'),
78510 paste_icon: icon('#iD-operation-paste', 'inline operation'),
78511 reflect_long_icon: icon('#iD-operation-reflect-long', 'inline operation'),
78512 reflect_short_icon: icon('#iD-operation-reflect-short', 'inline operation'),
78513 reverse_icon: icon('#iD-operation-reverse', 'inline operation'),
78514 rotate_icon: icon('#iD-operation-rotate', 'inline operation'),
78515 split_icon: icon('#iD-operation-split', 'inline operation'),
78516 straighten_icon: icon('#iD-operation-straighten', 'inline operation'),
78517 // interaction icons
78518 leftclick: icon('#iD-walkthrough-mouse-left', 'inline operation'),
78519 rightclick: icon('#iD-walkthrough-mouse-right', 'inline operation'),
78520 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'inline operation'),
78521 tap_icon: icon('#iD-walkthrough-tap', 'inline operation'),
78522 doubletap_icon: icon('#iD-walkthrough-doubletap', 'inline operation'),
78523 longpress_icon: icon('#iD-walkthrough-longpress', 'inline operation'),
78524 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'inline operation'),
78525 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'inline operation'),
78526 // insert keys; may be localized and platform-dependent
78527 shift: uiCmd.display('⇧'),
78528 alt: uiCmd.display('⌥'),
78529 "return": uiCmd.display('↵'),
78530 esc: _t.html('shortcuts.key.esc'),
78531 space: _t.html('shortcuts.key.space'),
78532 add_note_key: _t.html('modes.add_note.key'),
78533 help_key: _t.html('help.key'),
78534 shortcuts_key: _t.html('shortcuts.toggle.key'),
78535 // reference localized UI labels directly so that they'll always match
78536 save: _t.html('save.title'),
78537 undo: _t.html('undo.title'),
78538 redo: _t.html('redo.title'),
78539 upload: _t.html('commit.save'),
78540 point: _t.html('modes.add_point.title'),
78541 line: _t.html('modes.add_line.title'),
78542 area: _t.html('modes.add_area.title'),
78543 note: _t.html('modes.add_note.label'),
78544 circularize: _t.html('operations.circularize.title'),
78545 "continue": _t.html('operations.continue.title'),
78546 copy: _t.html('operations.copy.title'),
78547 "delete": _t.html('operations.delete.title'),
78548 disconnect: _t.html('operations.disconnect.title'),
78549 downgrade: _t.html('operations.downgrade.title'),
78550 extract: _t.html('operations.extract.title'),
78551 merge: _t.html('operations.merge.title'),
78552 move: _t.html('operations.move.title'),
78553 orthogonalize: _t.html('operations.orthogonalize.title'),
78554 paste: _t.html('operations.paste.title'),
78555 reflect_long: _t.html('operations.reflect.title.long'),
78556 reflect_short: _t.html('operations.reflect.title.short'),
78557 reverse: _t.html('operations.reverse.title'),
78558 rotate: _t.html('operations.rotate.title'),
78559 split: _t.html('operations.split.title'),
78560 straighten: _t.html('operations.straighten.title'),
78561 map_data: _t.html('map_data.title'),
78562 osm_notes: _t.html('map_data.layers.notes.title'),
78563 fields: _t.html('inspector.fields'),
78564 tags: _t.html('inspector.tags'),
78565 relations: _t.html('inspector.relations'),
78566 new_relation: _t.html('inspector.new_relation'),
78567 turn_restrictions: _t.html('presets.fields.restrictions.label'),
78568 background_settings: _t.html('background.description'),
78569 imagery_offset: _t.html('background.fix_misalignment'),
78570 start_the_walkthrough: _t.html('splash.walkthrough'),
78571 help: _t.html('help.title'),
78572 ok: _t.html('intro.ok')
78576 if (replacements) {
78577 reps = Object.assign(replacements, helpStringReplacements);
78579 reps = helpStringReplacements;
78582 return _t.html(id, reps) // use keyboard key styling for shortcuts
78583 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
78586 function slugify(text) {
78587 return text.toString().toLowerCase().replace(/\s+/g, '-') // Replace spaces with -
78588 .replace(/[^\w\-]+/g, '') // Remove all non-word chars
78589 .replace(/\-\-+/g, '-') // Replace multiple - with single -
78590 .replace(/^-+/, '') // Trim - from start of text
78591 .replace(/-+$/, ''); // Trim - from end of text
78592 } // console warning for missing walkthrough names
78595 var missingStrings = {};
78597 function checkKey(key, text) {
78599 "default": undefined
78600 }) === undefined) {
78601 if (missingStrings.hasOwnProperty(key)) return; // warn once
78603 missingStrings[key] = text;
78604 var missing = key + ': ' + text;
78605 if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
78609 function localize(obj) {
78610 var key; // Assign name if entity has one..
78612 var name = obj.tags && obj.tags.name;
78615 key = 'intro.graph.name.' + slugify(name);
78616 obj.tags.name = _t(key, {
78619 checkKey(key, name);
78620 } // Assign street name if entity has one..
78623 var street = obj.tags && obj.tags['addr:street'];
78626 key = 'intro.graph.name.' + slugify(street);
78627 obj.tags['addr:street'] = _t(key, {
78630 checkKey(key, street); // Add address details common across walkthrough..
78632 var addrTags = ['block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'];
78633 addrTags.forEach(function (k) {
78634 var key = 'intro.graph.' + k;
78635 var tag = 'addr:' + k;
78636 var val = obj.tags && obj.tags[tag];
78637 var str = _t(key, {
78642 if (str.match(/^<.*>$/) !== null) {
78643 delete obj.tags[tag];
78645 obj.tags[tag] = str;
78652 } // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
78654 function isMostlySquare(points) {
78655 // note: uses 15 here instead of the 12 from actionOrthogonalize because
78656 // actionOrthogonalize can actually straighten some larger angles as it iterates
78657 var threshold = 15; // degrees within right or straight
78659 var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right
78661 var upperBound = Math.cos(threshold * Math.PI / 180); // near straight
78663 for (var i = 0; i < points.length; i++) {
78664 var a = points[(i - 1 + points.length) % points.length];
78665 var origin = points[i];
78666 var b = points[(i + 1) % points.length];
78667 var dotp = geoVecNormalizedDot(a, b, origin);
78668 var mag = Math.abs(dotp);
78670 if (mag > lowerBound && mag < upperBound) {
78677 function selectMenuItem(context, operation) {
78678 return context.container().select('.edit-menu .edit-menu-item-' + operation);
78680 function transitionTime(point1, point2) {
78681 var distance = geoSphericalDistance(point1, point2);
78682 if (distance === 0) return 0;else if (distance < 80) return 500;else return 1000;
78685 function uiCurtain(containerNode) {
78686 var surface = select(null),
78687 tooltip = select(null),
78688 darkness = select(null);
78690 function curtain(selection) {
78691 surface = selection.append('svg').attr('class', 'curtain').style('top', 0).style('left', 0);
78692 darkness = surface.append('path').attr('x', 0).attr('y', 0).attr('class', 'curtain-darkness');
78693 select(window).on('resize.curtain', resize);
78694 tooltip = selection.append('div').attr('class', 'tooltip');
78695 tooltip.append('div').attr('class', 'popover-arrow');
78696 tooltip.append('div').attr('class', 'popover-inner');
78699 function resize() {
78700 surface.attr('width', containerNode.clientWidth).attr('height', containerNode.clientHeight);
78701 curtain.cut(darkness.datum());
78705 * Reveal cuts the curtain to highlight the given box,
78706 * and shows a tooltip with instructions next to the box.
78708 * @param {String|ClientRect} [box] box used to cut the curtain
78709 * @param {String} [text] text for a tooltip
78710 * @param {Object} [options]
78711 * @param {string} [options.tooltipClass] optional class to add to the tooltip
78712 * @param {integer} [options.duration] transition time in milliseconds
78713 * @param {string} [options.buttonText] if set, create a button with this text label
78714 * @param {function} [options.buttonCallback] if set, the callback for the button
78715 * @param {function} [options.padding] extra margin in px to put around bbox
78716 * @param {String|ClientRect} [options.tooltipBox] box for tooltip position, if different from box for the curtain
78720 curtain.reveal = function (box, html, options) {
78721 options = options || {};
78723 if (typeof box === 'string') {
78724 box = select(box).node();
78727 if (box && box.getBoundingClientRect) {
78728 box = copyBox(box.getBoundingClientRect());
78729 var containerRect = containerNode.getBoundingClientRect();
78730 box.top -= containerRect.top;
78731 box.left -= containerRect.left;
78734 if (box && options.padding) {
78735 box.top -= options.padding;
78736 box.left -= options.padding;
78737 box.bottom += options.padding;
78738 box.right += options.padding;
78739 box.height += options.padding * 2;
78740 box.width += options.padding * 2;
78745 if (options.tooltipBox) {
78746 tooltipBox = options.tooltipBox;
78748 if (typeof tooltipBox === 'string') {
78749 tooltipBox = select(tooltipBox).node();
78752 if (tooltipBox && tooltipBox.getBoundingClientRect) {
78753 tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
78759 if (tooltipBox && html) {
78760 if (html.indexOf('**') !== -1) {
78761 if (html.indexOf('<span') === 0) {
78762 html = html.replace(/^(<span.*?>)(.+?)(\*\*)/, '$1<span>$2</span>$3');
78764 html = html.replace(/^(.+?)(\*\*)/, '<span>$1</span>$2');
78765 } // pseudo markdown bold text for the instruction section..
78768 html = html.replace(/\*\*(.*?)\*\*/g, '<span class="instruction">$1</span>');
78771 html = html.replace(/\*(.*?)\*/g, '<em>$1</em>'); // emphasis
78773 html = html.replace(/\{br\}/g, '<br/><br/>'); // linebreak
78775 if (options.buttonText && options.buttonCallback) {
78776 html += '<div class="button-section">' + '<button href="#" class="button action">' + options.buttonText + '</button></div>';
78779 var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
78780 tooltip.classed(classes, true).selectAll('.popover-inner').html(html);
78782 if (options.buttonText && options.buttonCallback) {
78783 var button = tooltip.selectAll('.button-section .button.action');
78784 button.on('click', function (d3_event) {
78785 d3_event.preventDefault();
78786 options.buttonCallback();
78790 var tip = copyBox(tooltip.node().getBoundingClientRect()),
78791 w = containerNode.clientWidth,
78792 h = containerNode.clientHeight,
78793 tooltipWidth = 200,
78796 pos; // hack: this will have bottom placement,
78797 // so need to reserve extra space for the tooltip illustration.
78799 if (options.tooltipClass === 'intro-mouse') {
78801 } // trim box dimensions to just the portion that fits in the container..
78804 if (tooltipBox.top + tooltipBox.height > h) {
78805 tooltipBox.height -= tooltipBox.top + tooltipBox.height - h;
78808 if (tooltipBox.left + tooltipBox.width > w) {
78809 tooltipBox.width -= tooltipBox.left + tooltipBox.width - w;
78810 } // determine tooltip placement..
78813 if (tooltipBox.top + tooltipBox.height < 100) {
78814 // tooltip below box..
78816 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top + tooltipBox.height];
78817 } else if (tooltipBox.top > h - 140) {
78818 // tooltip above box..
78820 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top - tip.height];
78822 // tooltip to the side of the tooltipBox..
78823 var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
78825 if (_mainLocalizer.textDirection() === 'rtl') {
78826 if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
78828 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
78831 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
78834 if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
78836 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
78839 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
78844 if (options.duration !== 0 || !tooltip.classed(side)) {
78845 tooltip.call(uiToggle(true));
78848 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
78849 // (doesn't affect the placement of the popover-arrow)
78853 if (side === 'left' || side === 'right') {
78855 shiftY = 60 - pos[1];
78856 } else if (pos[1] + tip.height > h - 100) {
78857 shiftY = h - pos[1] - tip.height - 100;
78861 tooltip.selectAll('.popover-inner').style('top', shiftY + 'px');
78863 tooltip.classed('in', false).call(uiToggle(false));
78866 curtain.cut(box, options.duration);
78870 curtain.cut = function (datum, duration) {
78871 darkness.datum(datum).interrupt();
78874 if (duration === 0) {
78875 selection = darkness;
78877 selection = darkness.transition().duration(duration || 600).ease(linear$1);
78880 selection.attr('d', function (d) {
78881 var containerWidth = containerNode.clientWidth;
78882 var containerHeight = containerNode.clientHeight;
78883 var string = 'M 0,0 L 0,' + containerHeight + ' L ' + containerWidth + ',' + containerHeight + 'L' + containerWidth + ',0 Z';
78884 if (!d) return string;
78885 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';
78889 curtain.remove = function () {
78892 select(window).on('resize.curtain', null);
78893 }; // ClientRects are immutable, so copy them to an object,
78894 // in case we need to trim the height/width.
78897 function copyBox(src) {
78901 bottom: src.bottom,
78911 function uiIntroWelcome(context, reveal) {
78912 var dispatch$1 = dispatch('done');
78914 title: 'intro.welcome.title'
78917 function welcome() {
78918 context.map().centerZoom([-85.63591, 41.94285], 19);
78919 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.welcome'), {
78920 buttonText: _t.html('intro.ok'),
78921 buttonCallback: practice
78925 function practice() {
78926 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.practice'), {
78927 buttonText: _t.html('intro.ok'),
78928 buttonCallback: words
78933 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.words'), {
78934 buttonText: _t.html('intro.ok'),
78935 buttonCallback: chapters
78939 function chapters() {
78940 dispatch$1.call('done');
78941 reveal('.intro-nav-wrap .chapter-navigation', helpHtml('intro.welcome.chapters', {
78942 next: _t('intro.navigation.title')
78946 chapter.enter = function () {
78950 chapter.exit = function () {
78951 context.container().select('.curtain-tooltip.intro-mouse').selectAll('.counter').remove();
78954 chapter.restart = function () {
78959 return utilRebind(chapter, dispatch$1, 'on');
78962 function uiIntroNavigation(context, reveal) {
78963 var dispatch$1 = dispatch('done');
78965 var hallId = 'n2061';
78966 var townHall = [-85.63591, 41.94285];
78967 var springStreetId = 'w397';
78968 var springStreetEndId = 'n1834';
78969 var springStreet = [-85.63582, 41.94255];
78970 var onewayField = _mainPresetIndex.field('oneway');
78971 var maxspeedField = _mainPresetIndex.field('maxspeed');
78973 title: 'intro.navigation.title'
78976 function timeout(f, t) {
78977 timeouts.push(window.setTimeout(f, t));
78980 function eventCancel(d3_event) {
78981 d3_event.stopPropagation();
78982 d3_event.preventDefault();
78985 function isTownHallSelected() {
78986 var ids = context.selectedIDs();
78987 return ids.length === 1 && ids[0] === hallId;
78990 function dragMap() {
78991 context.enter(modeBrowse(context));
78992 context.history().reset('initial');
78993 var msec = transitionTime(townHall, context.map().center());
78996 reveal(null, null, {
79001 context.map().centerZoomEase(townHall, 19, msec);
79002 timeout(function () {
79003 var centerStart = context.map().center();
79004 var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
79005 var dragString = helpHtml('intro.navigation.map_info') + '{br}' + helpHtml('intro.navigation.' + textId);
79006 reveal('.surface', dragString);
79007 context.map().on('drawn.intro', function () {
79008 reveal('.surface', dragString, {
79012 context.map().on('move.intro', function () {
79013 var centerNow = context.map().center();
79015 if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
79016 context.map().on('move.intro', null);
79017 timeout(function () {
79018 continueTo(zoomMap);
79024 function continueTo(nextStep) {
79025 context.map().on('move.intro drawn.intro', null);
79030 function zoomMap() {
79031 var zoomStart = context.map().zoom();
79032 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
79033 var zoomString = helpHtml('intro.navigation.' + textId);
79034 reveal('.surface', zoomString);
79035 context.map().on('drawn.intro', function () {
79036 reveal('.surface', zoomString, {
79040 context.map().on('move.intro', function () {
79041 if (context.map().zoom() !== zoomStart) {
79042 context.map().on('move.intro', null);
79043 timeout(function () {
79044 continueTo(features);
79049 function continueTo(nextStep) {
79050 context.map().on('move.intro drawn.intro', null);
79055 function features() {
79056 var onClick = function onClick() {
79057 continueTo(pointsLinesAreas);
79060 reveal('.surface', helpHtml('intro.navigation.features'), {
79061 buttonText: _t.html('intro.ok'),
79062 buttonCallback: onClick
79064 context.map().on('drawn.intro', function () {
79065 reveal('.surface', helpHtml('intro.navigation.features'), {
79067 buttonText: _t.html('intro.ok'),
79068 buttonCallback: onClick
79072 function continueTo(nextStep) {
79073 context.map().on('drawn.intro', null);
79078 function pointsLinesAreas() {
79079 var onClick = function onClick() {
79080 continueTo(nodesWays);
79083 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
79084 buttonText: _t.html('intro.ok'),
79085 buttonCallback: onClick
79087 context.map().on('drawn.intro', function () {
79088 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
79090 buttonText: _t.html('intro.ok'),
79091 buttonCallback: onClick
79095 function continueTo(nextStep) {
79096 context.map().on('drawn.intro', null);
79101 function nodesWays() {
79102 var onClick = function onClick() {
79103 continueTo(clickTownHall);
79106 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
79107 buttonText: _t.html('intro.ok'),
79108 buttonCallback: onClick
79110 context.map().on('drawn.intro', function () {
79111 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
79113 buttonText: _t.html('intro.ok'),
79114 buttonCallback: onClick
79118 function continueTo(nextStep) {
79119 context.map().on('drawn.intro', null);
79124 function clickTownHall() {
79125 context.enter(modeBrowse(context));
79126 context.history().reset('initial');
79127 var entity = context.hasEntity(hallId);
79128 if (!entity) return;
79129 reveal(null, null, {
79132 context.map().centerZoomEase(entity.loc, 19, 500);
79133 timeout(function () {
79134 var entity = context.hasEntity(hallId);
79135 if (!entity) return;
79136 var box = pointBox(entity.loc, context);
79137 var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
79138 reveal(box, helpHtml('intro.navigation.' + textId));
79139 context.map().on('move.intro drawn.intro', function () {
79140 var entity = context.hasEntity(hallId);
79141 if (!entity) return;
79142 var box = pointBox(entity.loc, context);
79143 reveal(box, helpHtml('intro.navigation.' + textId), {
79147 context.on('enter.intro', function () {
79148 if (isTownHallSelected()) continueTo(selectedTownHall);
79150 }, 550); // after centerZoomEase
79152 context.history().on('change.intro', function () {
79153 if (!context.hasEntity(hallId)) {
79154 continueTo(clickTownHall);
79158 function continueTo(nextStep) {
79159 context.on('enter.intro', null);
79160 context.map().on('move.intro drawn.intro', null);
79161 context.history().on('change.intro', null);
79166 function selectedTownHall() {
79167 if (!isTownHallSelected()) return clickTownHall();
79168 var entity = context.hasEntity(hallId);
79169 if (!entity) return clickTownHall();
79170 var box = pointBox(entity.loc, context);
79172 var onClick = function onClick() {
79173 continueTo(editorTownHall);
79176 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
79177 buttonText: _t.html('intro.ok'),
79178 buttonCallback: onClick
79180 context.map().on('move.intro drawn.intro', function () {
79181 var entity = context.hasEntity(hallId);
79182 if (!entity) return;
79183 var box = pointBox(entity.loc, context);
79184 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
79186 buttonText: _t.html('intro.ok'),
79187 buttonCallback: onClick
79190 context.history().on('change.intro', function () {
79191 if (!context.hasEntity(hallId)) {
79192 continueTo(clickTownHall);
79196 function continueTo(nextStep) {
79197 context.map().on('move.intro drawn.intro', null);
79198 context.history().on('change.intro', null);
79203 function editorTownHall() {
79204 if (!isTownHallSelected()) return clickTownHall(); // disallow scrolling
79206 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79208 var onClick = function onClick() {
79209 continueTo(presetTownHall);
79212 reveal('.entity-editor-pane', helpHtml('intro.navigation.editor_townhall'), {
79213 buttonText: _t.html('intro.ok'),
79214 buttonCallback: onClick
79216 context.on('exit.intro', function () {
79217 continueTo(clickTownHall);
79219 context.history().on('change.intro', function () {
79220 if (!context.hasEntity(hallId)) {
79221 continueTo(clickTownHall);
79225 function continueTo(nextStep) {
79226 context.on('exit.intro', null);
79227 context.history().on('change.intro', null);
79228 context.container().select('.inspector-wrap').on('wheel.intro', null);
79233 function presetTownHall() {
79234 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
79236 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
79238 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); // preset match, in case the user happened to change it.
79240 var entity = context.entity(context.selectedIDs()[0]);
79241 var preset = _mainPresetIndex.match(entity, context.graph());
79243 var onClick = function onClick() {
79244 continueTo(fieldsTownHall);
79247 reveal('.entity-editor-pane .section-feature-type', helpHtml('intro.navigation.preset_townhall', {
79248 preset: preset.name()
79250 buttonText: _t.html('intro.ok'),
79251 buttonCallback: onClick
79253 context.on('exit.intro', function () {
79254 continueTo(clickTownHall);
79256 context.history().on('change.intro', function () {
79257 if (!context.hasEntity(hallId)) {
79258 continueTo(clickTownHall);
79262 function continueTo(nextStep) {
79263 context.on('exit.intro', null);
79264 context.history().on('change.intro', null);
79265 context.container().select('.inspector-wrap').on('wheel.intro', null);
79270 function fieldsTownHall() {
79271 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
79273 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
79275 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79277 var onClick = function onClick() {
79278 continueTo(closeTownHall);
79281 reveal('.entity-editor-pane .section-preset-fields', helpHtml('intro.navigation.fields_townhall'), {
79282 buttonText: _t.html('intro.ok'),
79283 buttonCallback: onClick
79285 context.on('exit.intro', function () {
79286 continueTo(clickTownHall);
79288 context.history().on('change.intro', function () {
79289 if (!context.hasEntity(hallId)) {
79290 continueTo(clickTownHall);
79294 function continueTo(nextStep) {
79295 context.on('exit.intro', null);
79296 context.history().on('change.intro', null);
79297 context.container().select('.inspector-wrap').on('wheel.intro', null);
79302 function closeTownHall() {
79303 if (!isTownHallSelected()) return clickTownHall();
79304 var selector = '.entity-editor-pane button.close svg use';
79305 var href = select(selector).attr('href') || '#iD-icon-close';
79306 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
79307 button: icon(href, 'inline')
79309 context.on('exit.intro', function () {
79310 continueTo(searchStreet);
79312 context.history().on('change.intro', function () {
79313 // update the close icon in the tooltip if the user edits something.
79314 var selector = '.entity-editor-pane button.close svg use';
79315 var href = select(selector).attr('href') || '#iD-icon-close';
79316 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
79317 button: icon(href, 'inline')
79323 function continueTo(nextStep) {
79324 context.on('exit.intro', null);
79325 context.history().on('change.intro', null);
79330 function searchStreet() {
79331 context.enter(modeBrowse(context));
79332 context.history().reset('initial'); // ensure spring street exists
79334 var msec = transitionTime(springStreet, context.map().center());
79337 reveal(null, null, {
79342 context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it
79344 timeout(function () {
79345 reveal('.search-header input', helpHtml('intro.navigation.search_street', {
79346 name: _t('intro.graph.name.spring-street')
79348 context.container().select('.search-header input').on('keyup.intro', checkSearchResult);
79352 function checkSearchResult() {
79353 var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item
79355 var firstName = first.select('.entity-name');
79356 var name = _t('intro.graph.name.spring-street');
79358 if (!firstName.empty() && firstName.html() === name) {
79359 reveal(first.node(), helpHtml('intro.navigation.choose_street', {
79364 context.on('exit.intro', function () {
79365 continueTo(selectedStreet);
79367 context.container().select('.search-header input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
79370 function continueTo(nextStep) {
79371 context.on('exit.intro', null);
79372 context.container().select('.search-header input').on('keydown.intro', null).on('keyup.intro', null);
79377 function selectedStreet() {
79378 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
79379 return searchStreet();
79382 var onClick = function onClick() {
79383 continueTo(editorStreet);
79386 var entity = context.entity(springStreetEndId);
79387 var box = pointBox(entity.loc, context);
79389 reveal(box, helpHtml('intro.navigation.selected_street', {
79390 name: _t('intro.graph.name.spring-street')
79393 buttonText: _t.html('intro.ok'),
79394 buttonCallback: onClick
79396 timeout(function () {
79397 context.map().on('move.intro drawn.intro', function () {
79398 var entity = context.hasEntity(springStreetEndId);
79399 if (!entity) return;
79400 var box = pointBox(entity.loc, context);
79402 reveal(box, helpHtml('intro.navigation.selected_street', {
79403 name: _t('intro.graph.name.spring-street')
79406 buttonText: _t.html('intro.ok'),
79407 buttonCallback: onClick
79410 }, 600); // after reveal.
79412 context.on('enter.intro', function (mode) {
79413 if (!context.hasEntity(springStreetId)) {
79414 return continueTo(searchStreet);
79417 var ids = context.selectedIDs();
79419 if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
79420 // keep Spring Street selected..
79421 context.enter(modeSelect(context, [springStreetId]));
79424 context.history().on('change.intro', function () {
79425 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
79426 timeout(function () {
79427 continueTo(searchStreet);
79428 }, 300); // after any transition (e.g. if user deleted intersection)
79432 function continueTo(nextStep) {
79433 context.map().on('move.intro drawn.intro', null);
79434 context.on('enter.intro', null);
79435 context.history().on('change.intro', null);
79440 function editorStreet() {
79441 var selector = '.entity-editor-pane button.close svg use';
79442 var href = select(selector).attr('href') || '#iD-icon-close';
79443 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
79444 button: icon(href, 'inline'),
79445 field1: onewayField.label(),
79446 field2: maxspeedField.label()
79448 context.on('exit.intro', function () {
79451 context.history().on('change.intro', function () {
79452 // update the close icon in the tooltip if the user edits something.
79453 var selector = '.entity-editor-pane button.close svg use';
79454 var href = select(selector).attr('href') || '#iD-icon-close';
79455 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
79456 button: icon(href, 'inline'),
79457 field1: onewayField.label(),
79458 field2: maxspeedField.label()
79464 function continueTo(nextStep) {
79465 context.on('exit.intro', null);
79466 context.history().on('change.intro', null);
79472 dispatch$1.call('done');
79473 reveal('.ideditor', helpHtml('intro.navigation.play', {
79474 next: _t('intro.points.title')
79476 tooltipBox: '.intro-nav-wrap .chapter-point',
79477 buttonText: _t.html('intro.ok'),
79478 buttonCallback: function buttonCallback() {
79479 reveal('.ideditor');
79484 chapter.enter = function () {
79488 chapter.exit = function () {
79489 timeouts.forEach(window.clearTimeout);
79490 context.on('enter.intro exit.intro', null);
79491 context.map().on('move.intro drawn.intro', null);
79492 context.history().on('change.intro', null);
79493 context.container().select('.inspector-wrap').on('wheel.intro', null);
79494 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
79497 chapter.restart = function () {
79502 return utilRebind(chapter, dispatch$1, 'on');
79505 function uiIntroPoint(context, reveal) {
79506 var dispatch$1 = dispatch('done');
79508 var intersection = [-85.63279, 41.94394];
79509 var building = [-85.632422, 41.944045];
79510 var cafePreset = _mainPresetIndex.item('amenity/cafe');
79511 var _pointID = null;
79513 title: 'intro.points.title'
79516 function timeout(f, t) {
79517 timeouts.push(window.setTimeout(f, t));
79520 function eventCancel(d3_event) {
79521 d3_event.stopPropagation();
79522 d3_event.preventDefault();
79525 function addPoint() {
79526 context.enter(modeBrowse(context));
79527 context.history().reset('initial');
79528 var msec = transitionTime(intersection, context.map().center());
79531 reveal(null, null, {
79536 context.map().centerZoomEase(intersection, 19, msec);
79537 timeout(function () {
79538 var tooltip = reveal('button.add-point', helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));
79540 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-points');
79541 context.on('enter.intro', function (mode) {
79542 if (mode.id !== 'add-point') return;
79543 continueTo(placePoint);
79547 function continueTo(nextStep) {
79548 context.on('enter.intro', null);
79553 function placePoint() {
79554 if (context.mode().id !== 'add-point') {
79555 return chapter.restart();
79558 var pointBox = pad(building, 150, context);
79559 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
79560 reveal(pointBox, helpHtml('intro.points.' + textId));
79561 context.map().on('move.intro drawn.intro', function () {
79562 pointBox = pad(building, 150, context);
79563 reveal(pointBox, helpHtml('intro.points.' + textId), {
79567 context.on('enter.intro', function (mode) {
79568 if (mode.id !== 'select') return chapter.restart();
79569 _pointID = context.mode().selectedIDs()[0];
79570 continueTo(searchPreset);
79573 function continueTo(nextStep) {
79574 context.map().on('move.intro drawn.intro', null);
79575 context.on('enter.intro', null);
79580 function searchPreset() {
79581 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79583 } // disallow scrolling
79586 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79587 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
79588 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
79589 preset: cafePreset.name()
79591 context.on('enter.intro', function (mode) {
79592 if (!_pointID || !context.hasEntity(_pointID)) {
79593 return continueTo(addPoint);
79596 var ids = context.selectedIDs();
79598 if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
79599 // keep the user's point selected..
79600 context.enter(modeSelect(context, [_pointID])); // disallow scrolling
79602 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79603 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
79604 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
79605 preset: cafePreset.name()
79607 context.history().on('change.intro', null);
79611 function checkPresetSearch() {
79612 var first = context.container().select('.preset-list-item:first-child');
79614 if (first.classed('preset-amenity-cafe')) {
79615 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
79616 reveal(first.select('.preset-list-button').node(), helpHtml('intro.points.choose_cafe', {
79617 preset: cafePreset.name()
79621 context.history().on('change.intro', function () {
79622 continueTo(aboutFeatureEditor);
79627 function continueTo(nextStep) {
79628 context.on('enter.intro', null);
79629 context.history().on('change.intro', null);
79630 context.container().select('.inspector-wrap').on('wheel.intro', null);
79631 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
79636 function aboutFeatureEditor() {
79637 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79641 timeout(function () {
79642 reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
79643 tooltipClass: 'intro-points-describe',
79644 buttonText: _t.html('intro.ok'),
79645 buttonCallback: function buttonCallback() {
79646 continueTo(addName);
79650 context.on('exit.intro', function () {
79651 // if user leaves select mode here, just continue with the tutorial.
79652 continueTo(reselectPoint);
79655 function continueTo(nextStep) {
79656 context.on('exit.intro', null);
79661 function addName() {
79662 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79664 } // reset pane, in case user happened to change it..
79667 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79668 var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name');
79669 timeout(function () {
79670 // It's possible for the user to add a name in a previous step..
79671 // If so, don't tell them to add the name in this step.
79672 // Give them an OK button instead.
79673 var entity = context.entity(_pointID);
79675 if (entity.tags.name) {
79676 var tooltip = reveal('.entity-editor-pane', addNameString, {
79677 tooltipClass: 'intro-points-describe',
79678 buttonText: _t.html('intro.ok'),
79679 buttonCallback: function buttonCallback() {
79680 continueTo(addCloseEditor);
79683 tooltip.select('.instruction').style('display', 'none');
79685 reveal('.entity-editor-pane', addNameString, {
79686 tooltipClass: 'intro-points-describe'
79690 context.history().on('change.intro', function () {
79691 continueTo(addCloseEditor);
79693 context.on('exit.intro', function () {
79694 // if user leaves select mode here, just continue with the tutorial.
79695 continueTo(reselectPoint);
79698 function continueTo(nextStep) {
79699 context.on('exit.intro', null);
79700 context.history().on('change.intro', null);
79705 function addCloseEditor() {
79706 // reset pane, in case user happened to change it..
79707 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79708 var selector = '.entity-editor-pane button.close svg use';
79709 var href = select(selector).attr('href') || '#iD-icon-close';
79710 context.on('exit.intro', function () {
79711 continueTo(reselectPoint);
79713 reveal('.entity-editor-pane', helpHtml('intro.points.add_close', {
79714 button: icon(href, 'inline')
79717 function continueTo(nextStep) {
79718 context.on('exit.intro', null);
79723 function reselectPoint() {
79724 if (!_pointID) return chapter.restart();
79725 var entity = context.hasEntity(_pointID);
79726 if (!entity) return chapter.restart(); // make sure it's still a cafe, in case user somehow changed it..
79728 var oldPreset = _mainPresetIndex.match(entity, context.graph());
79729 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
79730 context.enter(modeBrowse(context));
79731 var msec = transitionTime(entity.loc, context.map().center());
79734 reveal(null, null, {
79739 context.map().centerEase(entity.loc, msec);
79740 timeout(function () {
79741 var box = pointBox(entity.loc, context);
79742 reveal(box, helpHtml('intro.points.reselect'), {
79745 timeout(function () {
79746 context.map().on('move.intro drawn.intro', function () {
79747 var entity = context.hasEntity(_pointID);
79748 if (!entity) return chapter.restart();
79749 var box = pointBox(entity.loc, context);
79750 reveal(box, helpHtml('intro.points.reselect'), {
79754 }, 600); // after reveal..
79756 context.on('enter.intro', function (mode) {
79757 if (mode.id !== 'select') return;
79758 continueTo(updatePoint);
79762 function continueTo(nextStep) {
79763 context.map().on('move.intro drawn.intro', null);
79764 context.on('enter.intro', null);
79769 function updatePoint() {
79770 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79771 return continueTo(reselectPoint);
79772 } // reset pane, in case user happened to untag the point..
79775 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79776 context.on('exit.intro', function () {
79777 continueTo(reselectPoint);
79779 context.history().on('change.intro', function () {
79780 continueTo(updateCloseEditor);
79782 timeout(function () {
79783 reveal('.entity-editor-pane', helpHtml('intro.points.update'), {
79784 tooltipClass: 'intro-points-describe'
79788 function continueTo(nextStep) {
79789 context.on('exit.intro', null);
79790 context.history().on('change.intro', null);
79795 function updateCloseEditor() {
79796 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79797 return continueTo(reselectPoint);
79798 } // reset pane, in case user happened to change it..
79801 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79802 context.on('exit.intro', function () {
79803 continueTo(rightClickPoint);
79805 timeout(function () {
79806 reveal('.entity-editor-pane', helpHtml('intro.points.update_close', {
79807 button: icon('#iD-icon-close', 'inline')
79811 function continueTo(nextStep) {
79812 context.on('exit.intro', null);
79817 function rightClickPoint() {
79818 if (!_pointID) return chapter.restart();
79819 var entity = context.hasEntity(_pointID);
79820 if (!entity) return chapter.restart();
79821 context.enter(modeBrowse(context));
79822 var box = pointBox(entity.loc, context);
79823 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
79824 reveal(box, helpHtml('intro.points.' + textId), {
79827 timeout(function () {
79828 context.map().on('move.intro', function () {
79829 var entity = context.hasEntity(_pointID);
79830 if (!entity) return chapter.restart();
79831 var box = pointBox(entity.loc, context);
79832 reveal(box, helpHtml('intro.points.' + textId), {
79836 }, 600); // after reveal
79838 context.on('enter.intro', function (mode) {
79839 if (mode.id !== 'select') return;
79840 var ids = context.selectedIDs();
79841 if (ids.length !== 1 || ids[0] !== _pointID) return;
79842 timeout(function () {
79843 var node = selectMenuItem(context, 'delete').node();
79845 continueTo(enterDelete);
79846 }, 50); // after menu visible
79849 function continueTo(nextStep) {
79850 context.on('enter.intro', null);
79851 context.map().on('move.intro', null);
79856 function enterDelete() {
79857 if (!_pointID) return chapter.restart();
79858 var entity = context.hasEntity(_pointID);
79859 if (!entity) return chapter.restart();
79860 var node = selectMenuItem(context, 'delete').node();
79863 return continueTo(rightClickPoint);
79866 reveal('.edit-menu', helpHtml('intro.points.delete'), {
79869 timeout(function () {
79870 context.map().on('move.intro', function () {
79871 reveal('.edit-menu', helpHtml('intro.points.delete'), {
79876 }, 300); // after menu visible
79878 context.on('exit.intro', function () {
79879 if (!_pointID) return chapter.restart();
79880 var entity = context.hasEntity(_pointID);
79881 if (entity) return continueTo(rightClickPoint); // point still exists
79883 context.history().on('change.intro', function (changed) {
79884 if (changed.deleted().length) {
79889 function continueTo(nextStep) {
79890 context.map().on('move.intro', null);
79891 context.history().on('change.intro', null);
79892 context.on('exit.intro', null);
79898 context.history().on('change.intro', function () {
79901 reveal('.top-toolbar button.undo-button', helpHtml('intro.points.undo'));
79903 function continueTo(nextStep) {
79904 context.history().on('change.intro', null);
79910 dispatch$1.call('done');
79911 reveal('.ideditor', helpHtml('intro.points.play', {
79912 next: _t('intro.areas.title')
79914 tooltipBox: '.intro-nav-wrap .chapter-area',
79915 buttonText: _t.html('intro.ok'),
79916 buttonCallback: function buttonCallback() {
79917 reveal('.ideditor');
79922 chapter.enter = function () {
79926 chapter.exit = function () {
79927 timeouts.forEach(window.clearTimeout);
79928 context.on('enter.intro exit.intro', null);
79929 context.map().on('move.intro drawn.intro', null);
79930 context.history().on('change.intro', null);
79931 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79932 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
79935 chapter.restart = function () {
79940 return utilRebind(chapter, dispatch$1, 'on');
79943 function uiIntroArea(context, reveal) {
79944 var dispatch$1 = dispatch('done');
79945 var playground = [-85.63552, 41.94159];
79946 var playgroundPreset = _mainPresetIndex.item('leisure/playground');
79947 var nameField = _mainPresetIndex.field('name');
79948 var descriptionField = _mainPresetIndex.field('description');
79954 title: 'intro.areas.title'
79957 function timeout(f, t) {
79958 timeouts.push(window.setTimeout(f, t));
79961 function eventCancel(d3_event) {
79962 d3_event.stopPropagation();
79963 d3_event.preventDefault();
79966 function revealPlayground(center, text, options) {
79967 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
79968 var box = pad(center, padding, context);
79969 reveal(box, text, options);
79972 function addArea() {
79973 context.enter(modeBrowse(context));
79974 context.history().reset('initial');
79976 var msec = transitionTime(playground, context.map().center());
79979 reveal(null, null, {
79984 context.map().centerZoomEase(playground, 19, msec);
79985 timeout(function () {
79986 var tooltip = reveal('button.add-area', helpHtml('intro.areas.add_playground'));
79987 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-areas');
79988 context.on('enter.intro', function (mode) {
79989 if (mode.id !== 'add-area') return;
79990 continueTo(startPlayground);
79994 function continueTo(nextStep) {
79995 context.on('enter.intro', null);
80000 function startPlayground() {
80001 if (context.mode().id !== 'add-area') {
80002 return chapter.restart();
80006 context.map().zoomEase(19.5, 500);
80007 timeout(function () {
80008 var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
80009 var startDrawString = helpHtml('intro.areas.start_playground') + helpHtml('intro.areas.' + textId);
80010 revealPlayground(playground, startDrawString, {
80013 timeout(function () {
80014 context.map().on('move.intro drawn.intro', function () {
80015 revealPlayground(playground, startDrawString, {
80019 context.on('enter.intro', function (mode) {
80020 if (mode.id !== 'draw-area') return chapter.restart();
80021 continueTo(continuePlayground);
80023 }, 250); // after reveal
80024 }, 550); // after easing
80026 function continueTo(nextStep) {
80027 context.map().on('move.intro drawn.intro', null);
80028 context.on('enter.intro', null);
80033 function continuePlayground() {
80034 if (context.mode().id !== 'draw-area') {
80035 return chapter.restart();
80039 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
80042 timeout(function () {
80043 context.map().on('move.intro drawn.intro', function () {
80044 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
80048 }, 250); // after reveal
80050 context.on('enter.intro', function (mode) {
80051 if (mode.id === 'draw-area') {
80052 var entity = context.hasEntity(context.selectedIDs()[0]);
80054 if (entity && entity.nodes.length >= 6) {
80055 return continueTo(finishPlayground);
80059 } else if (mode.id === 'select') {
80060 _areaID = context.selectedIDs()[0];
80061 return continueTo(searchPresets);
80063 return chapter.restart();
80067 function continueTo(nextStep) {
80068 context.map().on('move.intro drawn.intro', null);
80069 context.on('enter.intro', null);
80074 function finishPlayground() {
80075 if (context.mode().id !== 'draw-area') {
80076 return chapter.restart();
80080 var finishString = helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.areas.finish_playground');
80081 revealPlayground(playground, finishString, {
80084 timeout(function () {
80085 context.map().on('move.intro drawn.intro', function () {
80086 revealPlayground(playground, finishString, {
80090 }, 250); // after reveal
80092 context.on('enter.intro', function (mode) {
80093 if (mode.id === 'draw-area') {
80095 } else if (mode.id === 'select') {
80096 _areaID = context.selectedIDs()[0];
80097 return continueTo(searchPresets);
80099 return chapter.restart();
80103 function continueTo(nextStep) {
80104 context.map().on('move.intro drawn.intro', null);
80105 context.on('enter.intro', null);
80110 function searchPresets() {
80111 if (!_areaID || !context.hasEntity(_areaID)) {
80115 var ids = context.selectedIDs();
80117 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80118 context.enter(modeSelect(context, [_areaID]));
80119 } // disallow scrolling
80122 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80123 timeout(function () {
80124 // reset pane, in case user somehow happened to change it..
80125 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80126 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
80127 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
80128 preset: playgroundPreset.name()
80130 }, 400); // after preset list pane visible..
80132 context.on('enter.intro', function (mode) {
80133 if (!_areaID || !context.hasEntity(_areaID)) {
80134 return continueTo(addArea);
80137 var ids = context.selectedIDs();
80139 if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
80140 // keep the user's area selected..
80141 context.enter(modeSelect(context, [_areaID])); // reset pane, in case user somehow happened to change it..
80143 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
80145 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80146 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
80147 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
80148 preset: playgroundPreset.name()
80150 context.history().on('change.intro', null);
80154 function checkPresetSearch() {
80155 var first = context.container().select('.preset-list-item:first-child');
80157 if (first.classed('preset-leisure-playground')) {
80158 reveal(first.select('.preset-list-button').node(), helpHtml('intro.areas.choose_playground', {
80159 preset: playgroundPreset.name()
80163 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
80164 context.history().on('change.intro', function () {
80165 continueTo(clickAddField);
80170 function continueTo(nextStep) {
80171 context.container().select('.inspector-wrap').on('wheel.intro', null);
80172 context.on('enter.intro', null);
80173 context.history().on('change.intro', null);
80174 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80179 function clickAddField() {
80180 if (!_areaID || !context.hasEntity(_areaID)) {
80184 var ids = context.selectedIDs();
80186 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80187 return searchPresets();
80190 if (!context.container().select('.form-field-description').empty()) {
80191 return continueTo(describePlayground);
80192 } // disallow scrolling
80195 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80196 timeout(function () {
80197 // reset pane, in case user somehow happened to change it..
80198 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // It's possible for the user to add a description in a previous step..
80199 // If they did this already, just continue to next step.
80201 var entity = context.entity(_areaID);
80203 if (entity.tags.description) {
80204 return continueTo(play);
80205 } // scroll "Add field" into view
80208 var box = context.container().select('.more-fields').node().getBoundingClientRect();
80210 if (box.top > 300) {
80211 var pane = context.container().select('.entity-editor-pane .inspector-body');
80212 var start = pane.node().scrollTop;
80213 var end = start + (box.top - 300);
80214 pane.transition().duration(250).tween('scroll.inspector', function () {
80216 var i = d3_interpolateNumber(start, end);
80217 return function (t) {
80218 node.scrollTop = i(t);
80223 timeout(function () {
80224 reveal('.more-fields .combobox-input', helpHtml('intro.areas.add_field', {
80225 name: nameField.label(),
80226 description: descriptionField.label()
80230 context.container().select('.more-fields .combobox-input').on('click.intro', function () {
80231 // Watch for the combobox to appear...
80233 watcher = window.setInterval(function () {
80234 if (!context.container().select('div.combobox').empty()) {
80235 window.clearInterval(watcher);
80236 continueTo(chooseDescriptionField);
80240 }, 300); // after "Add Field" visible
80241 }, 400); // after editor pane visible
80243 context.on('exit.intro', function () {
80244 return continueTo(searchPresets);
80247 function continueTo(nextStep) {
80248 context.container().select('.inspector-wrap').on('wheel.intro', null);
80249 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80250 context.on('exit.intro', null);
80255 function chooseDescriptionField() {
80256 if (!_areaID || !context.hasEntity(_areaID)) {
80260 var ids = context.selectedIDs();
80262 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80263 return searchPresets();
80266 if (!context.container().select('.form-field-description').empty()) {
80267 return continueTo(describePlayground);
80268 } // Make sure combobox is ready..
80271 if (context.container().select('div.combobox').empty()) {
80272 return continueTo(clickAddField);
80273 } // Watch for the combobox to go away..
80277 watcher = window.setInterval(function () {
80278 if (context.container().select('div.combobox').empty()) {
80279 window.clearInterval(watcher);
80280 timeout(function () {
80281 if (context.container().select('.form-field-description').empty()) {
80282 continueTo(retryChooseDescription);
80284 continueTo(describePlayground);
80286 }, 300); // after description field added.
80289 reveal('div.combobox', helpHtml('intro.areas.choose_field', {
80290 field: descriptionField.label()
80294 context.on('exit.intro', function () {
80295 return continueTo(searchPresets);
80298 function continueTo(nextStep) {
80299 if (watcher) window.clearInterval(watcher);
80300 context.on('exit.intro', null);
80305 function describePlayground() {
80306 if (!_areaID || !context.hasEntity(_areaID)) {
80310 var ids = context.selectedIDs();
80312 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80313 return searchPresets();
80314 } // reset pane, in case user happened to change it..
80317 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
80319 if (context.container().select('.form-field-description').empty()) {
80320 return continueTo(retryChooseDescription);
80323 context.on('exit.intro', function () {
80326 reveal('.entity-editor-pane', helpHtml('intro.areas.describe_playground', {
80327 button: icon('#iD-icon-close', 'inline')
80332 function continueTo(nextStep) {
80333 context.on('exit.intro', null);
80338 function retryChooseDescription() {
80339 if (!_areaID || !context.hasEntity(_areaID)) {
80343 var ids = context.selectedIDs();
80345 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80346 return searchPresets();
80347 } // reset pane, in case user happened to change it..
80350 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
80351 reveal('.entity-editor-pane', helpHtml('intro.areas.retry_add_field', {
80352 field: descriptionField.label()
80354 buttonText: _t.html('intro.ok'),
80355 buttonCallback: function buttonCallback() {
80356 continueTo(clickAddField);
80359 context.on('exit.intro', function () {
80360 return continueTo(searchPresets);
80363 function continueTo(nextStep) {
80364 context.on('exit.intro', null);
80370 dispatch$1.call('done');
80371 reveal('.ideditor', helpHtml('intro.areas.play', {
80372 next: _t('intro.lines.title')
80374 tooltipBox: '.intro-nav-wrap .chapter-line',
80375 buttonText: _t.html('intro.ok'),
80376 buttonCallback: function buttonCallback() {
80377 reveal('.ideditor');
80382 chapter.enter = function () {
80386 chapter.exit = function () {
80387 timeouts.forEach(window.clearTimeout);
80388 context.on('enter.intro exit.intro', null);
80389 context.map().on('move.intro drawn.intro', null);
80390 context.history().on('change.intro', null);
80391 context.container().select('.inspector-wrap').on('wheel.intro', null);
80392 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80393 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80396 chapter.restart = function () {
80401 return utilRebind(chapter, dispatch$1, 'on');
80404 function uiIntroLine(context, reveal) {
80405 var dispatch$1 = dispatch('done');
80407 var _tulipRoadID = null;
80408 var flowerRoadID = 'w646';
80409 var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
80410 var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
80411 var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
80412 var roadCategory = _mainPresetIndex.item('category-road_minor');
80413 var residentialPreset = _mainPresetIndex.item('highway/residential');
80414 var woodRoadID = 'w525';
80415 var woodRoadEndID = 'n2862';
80416 var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
80417 var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
80418 var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
80419 var washingtonStreetID = 'w522';
80420 var twelfthAvenueID = 'w1';
80421 var eleventhAvenueEndID = 'n3550';
80422 var twelfthAvenueEndID = 'n5';
80423 var _washingtonSegmentID = null;
80424 var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
80425 var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
80426 var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
80427 var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
80429 title: 'intro.lines.title'
80432 function timeout(f, t) {
80433 timeouts.push(window.setTimeout(f, t));
80436 function eventCancel(d3_event) {
80437 d3_event.stopPropagation();
80438 d3_event.preventDefault();
80441 function addLine() {
80442 context.enter(modeBrowse(context));
80443 context.history().reset('initial');
80444 var msec = transitionTime(tulipRoadStart, context.map().center());
80447 reveal(null, null, {
80452 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
80453 timeout(function () {
80454 var tooltip = reveal('button.add-line', helpHtml('intro.lines.add_line'));
80455 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-lines');
80456 context.on('enter.intro', function (mode) {
80457 if (mode.id !== 'add-line') return;
80458 continueTo(startLine);
80462 function continueTo(nextStep) {
80463 context.on('enter.intro', null);
80468 function startLine() {
80469 if (context.mode().id !== 'add-line') return chapter.restart();
80470 _tulipRoadID = null;
80471 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
80472 var box = pad(tulipRoadStart, padding, context);
80473 box.height = box.height + 100;
80474 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
80475 var startLineString = helpHtml('intro.lines.missing_road') + '{br}' + helpHtml('intro.lines.line_draw_info') + helpHtml('intro.lines.' + textId);
80476 reveal(box, startLineString);
80477 context.map().on('move.intro drawn.intro', function () {
80478 padding = 70 * Math.pow(2, context.map().zoom() - 18);
80479 box = pad(tulipRoadStart, padding, context);
80480 box.height = box.height + 100;
80481 reveal(box, startLineString, {
80485 context.on('enter.intro', function (mode) {
80486 if (mode.id !== 'draw-line') return chapter.restart();
80487 continueTo(drawLine);
80490 function continueTo(nextStep) {
80491 context.map().on('move.intro drawn.intro', null);
80492 context.on('enter.intro', null);
80497 function drawLine() {
80498 if (context.mode().id !== 'draw-line') return chapter.restart();
80499 _tulipRoadID = context.mode().selectedIDs()[0];
80500 context.map().centerEase(tulipRoadMidpoint, 500);
80501 timeout(function () {
80502 var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
80503 var box = pad(tulipRoadMidpoint, padding, context);
80504 box.height = box.height * 2;
80505 reveal(box, helpHtml('intro.lines.intersect', {
80506 name: _t('intro.graph.name.flower-street')
80508 context.map().on('move.intro drawn.intro', function () {
80509 padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
80510 box = pad(tulipRoadMidpoint, padding, context);
80511 box.height = box.height * 2;
80512 reveal(box, helpHtml('intro.lines.intersect', {
80513 name: _t('intro.graph.name.flower-street')
80518 }, 550); // after easing..
80520 context.history().on('change.intro', function () {
80521 if (isLineConnected()) {
80522 continueTo(continueLine);
80525 context.on('enter.intro', function (mode) {
80526 if (mode.id === 'draw-line') {
80528 } else if (mode.id === 'select') {
80529 continueTo(retryIntersect);
80532 return chapter.restart();
80536 function continueTo(nextStep) {
80537 context.map().on('move.intro drawn.intro', null);
80538 context.history().on('change.intro', null);
80539 context.on('enter.intro', null);
80544 function isLineConnected() {
80545 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
80547 if (!entity) return false;
80548 var drawNodes = context.graph().childNodes(entity);
80549 return drawNodes.some(function (node) {
80550 return context.graph().parentWays(node).some(function (parent) {
80551 return parent.id === flowerRoadID;
80556 function retryIntersect() {
80557 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
80558 var box = pad(tulipRoadIntersection, 80, context);
80559 reveal(box, helpHtml('intro.lines.retry_intersect', {
80560 name: _t('intro.graph.name.flower-street')
80562 timeout(chapter.restart, 3000);
80565 function continueLine() {
80566 if (context.mode().id !== 'draw-line') return chapter.restart();
80568 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
80570 if (!entity) return chapter.restart();
80571 context.map().centerEase(tulipRoadIntersection, 500);
80572 var continueLineText = helpHtml('intro.lines.continue_line') + '{br}' + helpHtml('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.lines.finish_road');
80573 reveal('.surface', continueLineText);
80574 context.on('enter.intro', function (mode) {
80575 if (mode.id === 'draw-line') return;else if (mode.id === 'select') return continueTo(chooseCategoryRoad);else return chapter.restart();
80578 function continueTo(nextStep) {
80579 context.on('enter.intro', null);
80584 function chooseCategoryRoad() {
80585 if (context.mode().id !== 'select') return chapter.restart();
80586 context.on('exit.intro', function () {
80587 return chapter.restart();
80589 var button = context.container().select('.preset-category-road_minor .preset-list-button');
80590 if (button.empty()) return chapter.restart(); // disallow scrolling
80592 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80593 timeout(function () {
80594 // reset pane, in case user somehow happened to change it..
80595 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80596 reveal(button.node(), helpHtml('intro.lines.choose_category_road', {
80597 category: roadCategory.name()
80599 button.on('click.intro', function () {
80600 continueTo(choosePresetResidential);
80602 }, 400); // after editor pane visible
80604 function continueTo(nextStep) {
80605 context.container().select('.inspector-wrap').on('wheel.intro', null);
80606 context.container().select('.preset-list-button').on('click.intro', null);
80607 context.on('exit.intro', null);
80612 function choosePresetResidential() {
80613 if (context.mode().id !== 'select') return chapter.restart();
80614 context.on('exit.intro', function () {
80615 return chapter.restart();
80617 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
80618 if (subgrid.empty()) return chapter.restart();
80619 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button').on('click.intro', function () {
80620 continueTo(retryPresetResidential);
80622 subgrid.selectAll('.preset-highway-residential .preset-list-button').on('click.intro', function () {
80623 continueTo(nameRoad);
80625 timeout(function () {
80626 reveal(subgrid.node(), helpHtml('intro.lines.choose_preset_residential', {
80627 preset: residentialPreset.name()
80629 tooltipBox: '.preset-highway-residential .preset-list-button',
80634 function continueTo(nextStep) {
80635 context.container().select('.preset-list-button').on('click.intro', null);
80636 context.on('exit.intro', null);
80639 } // selected wrong road type
80642 function retryPresetResidential() {
80643 if (context.mode().id !== 'select') return chapter.restart();
80644 context.on('exit.intro', function () {
80645 return chapter.restart();
80646 }); // disallow scrolling
80648 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80649 timeout(function () {
80650 var button = context.container().select('.entity-editor-pane .preset-list-button');
80651 reveal(button.node(), helpHtml('intro.lines.retry_preset_residential', {
80652 preset: residentialPreset.name()
80654 button.on('click.intro', function () {
80655 continueTo(chooseCategoryRoad);
80659 function continueTo(nextStep) {
80660 context.container().select('.inspector-wrap').on('wheel.intro', null);
80661 context.container().select('.preset-list-button').on('click.intro', null);
80662 context.on('exit.intro', null);
80667 function nameRoad() {
80668 context.on('exit.intro', function () {
80669 continueTo(didNameRoad);
80671 timeout(function () {
80672 reveal('.entity-editor-pane', helpHtml('intro.lines.name_road', {
80673 button: icon('#iD-icon-close', 'inline')
80675 tooltipClass: 'intro-lines-name_road'
80679 function continueTo(nextStep) {
80680 context.on('exit.intro', null);
80685 function didNameRoad() {
80686 context.history().checkpoint('doneAddLine');
80687 timeout(function () {
80688 reveal('.surface', helpHtml('intro.lines.did_name_road'), {
80689 buttonText: _t.html('intro.ok'),
80690 buttonCallback: function buttonCallback() {
80691 continueTo(updateLine);
80696 function continueTo(nextStep) {
80701 function updateLine() {
80702 context.history().reset('doneAddLine');
80704 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80705 return chapter.restart();
80708 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
80711 reveal(null, null, {
80716 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
80717 timeout(function () {
80718 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
80719 var box = pad(woodRoadDragMidpoint, padding, context);
80721 var advance = function advance() {
80722 continueTo(addNode);
80725 reveal(box, helpHtml('intro.lines.update_line'), {
80726 buttonText: _t.html('intro.ok'),
80727 buttonCallback: advance
80729 context.map().on('move.intro drawn.intro', function () {
80730 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
80731 var box = pad(woodRoadDragMidpoint, padding, context);
80732 reveal(box, helpHtml('intro.lines.update_line'), {
80734 buttonText: _t.html('intro.ok'),
80735 buttonCallback: advance
80740 function continueTo(nextStep) {
80741 context.map().on('move.intro drawn.intro', null);
80746 function addNode() {
80747 context.history().reset('doneAddLine');
80749 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80750 return chapter.restart();
80753 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
80754 var box = pad(woodRoadAddNode, padding, context);
80755 var addNodeString = helpHtml('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
80756 reveal(box, addNodeString);
80757 context.map().on('move.intro drawn.intro', function () {
80758 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
80759 var box = pad(woodRoadAddNode, padding, context);
80760 reveal(box, addNodeString, {
80764 context.history().on('change.intro', function (changed) {
80765 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80766 return continueTo(updateLine);
80769 if (changed.created().length === 1) {
80770 timeout(function () {
80771 continueTo(startDragEndpoint);
80775 context.on('enter.intro', function (mode) {
80776 if (mode.id !== 'select') {
80777 continueTo(updateLine);
80781 function continueTo(nextStep) {
80782 context.map().on('move.intro drawn.intro', null);
80783 context.history().on('change.intro', null);
80784 context.on('enter.intro', null);
80789 function startDragEndpoint() {
80790 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80791 return continueTo(updateLine);
80794 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80795 var box = pad(woodRoadDragEndpoint, padding, context);
80796 var startDragString = helpHtml('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + helpHtml('intro.lines.drag_to_intersection');
80797 reveal(box, startDragString);
80798 context.map().on('move.intro drawn.intro', function () {
80799 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80800 return continueTo(updateLine);
80803 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80804 var box = pad(woodRoadDragEndpoint, padding, context);
80805 reveal(box, startDragString, {
80808 var entity = context.entity(woodRoadEndID);
80810 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
80811 continueTo(finishDragEndpoint);
80815 function continueTo(nextStep) {
80816 context.map().on('move.intro drawn.intro', null);
80821 function finishDragEndpoint() {
80822 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80823 return continueTo(updateLine);
80826 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80827 var box = pad(woodRoadDragEndpoint, padding, context);
80828 var finishDragString = helpHtml('intro.lines.spot_looks_good') + helpHtml('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
80829 reveal(box, finishDragString);
80830 context.map().on('move.intro drawn.intro', function () {
80831 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80832 return continueTo(updateLine);
80835 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80836 var box = pad(woodRoadDragEndpoint, padding, context);
80837 reveal(box, finishDragString, {
80840 var entity = context.entity(woodRoadEndID);
80842 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
80843 continueTo(startDragEndpoint);
80846 context.on('enter.intro', function () {
80847 continueTo(startDragMidpoint);
80850 function continueTo(nextStep) {
80851 context.map().on('move.intro drawn.intro', null);
80852 context.on('enter.intro', null);
80857 function startDragMidpoint() {
80858 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80859 return continueTo(updateLine);
80862 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
80863 context.enter(modeSelect(context, [woodRoadID]));
80866 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
80867 var box = pad(woodRoadDragMidpoint, padding, context);
80868 reveal(box, helpHtml('intro.lines.start_drag_midpoint'));
80869 context.map().on('move.intro drawn.intro', function () {
80870 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80871 return continueTo(updateLine);
80874 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
80875 var box = pad(woodRoadDragMidpoint, padding, context);
80876 reveal(box, helpHtml('intro.lines.start_drag_midpoint'), {
80880 context.history().on('change.intro', function (changed) {
80881 if (changed.created().length === 1) {
80882 continueTo(continueDragMidpoint);
80885 context.on('enter.intro', function (mode) {
80886 if (mode.id !== 'select') {
80887 // keep Wood Road selected so midpoint triangles are drawn..
80888 context.enter(modeSelect(context, [woodRoadID]));
80892 function continueTo(nextStep) {
80893 context.map().on('move.intro drawn.intro', null);
80894 context.history().on('change.intro', null);
80895 context.on('enter.intro', null);
80900 function continueDragMidpoint() {
80901 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80902 return continueTo(updateLine);
80905 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80906 var box = pad(woodRoadDragEndpoint, padding, context);
80909 var advance = function advance() {
80910 context.history().checkpoint('doneUpdateLine');
80911 continueTo(deleteLines);
80914 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
80915 buttonText: _t.html('intro.ok'),
80916 buttonCallback: advance
80918 context.map().on('move.intro drawn.intro', function () {
80919 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80920 return continueTo(updateLine);
80923 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80924 var box = pad(woodRoadDragEndpoint, padding, context);
80926 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
80928 buttonText: _t.html('intro.ok'),
80929 buttonCallback: advance
80933 function continueTo(nextStep) {
80934 context.map().on('move.intro drawn.intro', null);
80939 function deleteLines() {
80940 context.history().reset('doneUpdateLine');
80941 context.enter(modeBrowse(context));
80943 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
80944 return chapter.restart();
80947 var msec = transitionTime(deleteLinesLoc, context.map().center());
80950 reveal(null, null, {
80955 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
80956 timeout(function () {
80957 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80958 var box = pad(deleteLinesLoc, padding, context);
80962 var advance = function advance() {
80963 continueTo(rightClickIntersection);
80966 reveal(box, helpHtml('intro.lines.delete_lines', {
80967 street: _t('intro.graph.name.12th-avenue')
80969 buttonText: _t.html('intro.ok'),
80970 buttonCallback: advance
80972 context.map().on('move.intro drawn.intro', function () {
80973 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
80974 var box = pad(deleteLinesLoc, padding, context);
80977 reveal(box, helpHtml('intro.lines.delete_lines', {
80978 street: _t('intro.graph.name.12th-avenue')
80981 buttonText: _t.html('intro.ok'),
80982 buttonCallback: advance
80985 context.history().on('change.intro', function () {
80986 timeout(function () {
80987 continueTo(deleteLines);
80988 }, 500); // after any transition (e.g. if user deleted intersection)
80992 function continueTo(nextStep) {
80993 context.map().on('move.intro drawn.intro', null);
80994 context.history().on('change.intro', null);
80999 function rightClickIntersection() {
81000 context.history().reset('doneUpdateLine');
81001 context.enter(modeBrowse(context));
81002 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
81003 var rightClickString = helpHtml('intro.lines.split_street', {
81004 street1: _t('intro.graph.name.11th-avenue'),
81005 street2: _t('intro.graph.name.washington-street')
81006 }) + helpHtml('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
81007 timeout(function () {
81008 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81009 var box = pad(eleventhAvenueEnd, padding, context);
81010 reveal(box, rightClickString);
81011 context.map().on('move.intro drawn.intro', function () {
81012 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81013 var box = pad(eleventhAvenueEnd, padding, context);
81014 reveal(box, rightClickString, {
81018 context.on('enter.intro', function (mode) {
81019 if (mode.id !== 'select') return;
81020 var ids = context.selectedIDs();
81021 if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
81022 timeout(function () {
81023 var node = selectMenuItem(context, 'split').node();
81025 continueTo(splitIntersection);
81026 }, 50); // after menu visible
81028 context.history().on('change.intro', function () {
81029 timeout(function () {
81030 continueTo(deleteLines);
81031 }, 300); // after any transition (e.g. if user deleted intersection)
81035 function continueTo(nextStep) {
81036 context.map().on('move.intro drawn.intro', null);
81037 context.on('enter.intro', null);
81038 context.history().on('change.intro', null);
81043 function splitIntersection() {
81044 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81045 return continueTo(deleteLines);
81048 var node = selectMenuItem(context, 'split').node();
81051 return continueTo(rightClickIntersection);
81054 var wasChanged = false;
81055 _washingtonSegmentID = null;
81056 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
81057 street: _t('intro.graph.name.washington-street')
81061 context.map().on('move.intro drawn.intro', function () {
81062 var node = selectMenuItem(context, 'split').node();
81064 if (!wasChanged && !node) {
81065 return continueTo(rightClickIntersection);
81068 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
81069 street: _t('intro.graph.name.washington-street')
81075 context.history().on('change.intro', function (changed) {
81077 timeout(function () {
81078 if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
81081 _washingtonSegmentID = changed.created()[0].id;
81082 continueTo(didSplit);
81084 _washingtonSegmentID = null;
81085 continueTo(retrySplit);
81087 }, 300); // after any transition (e.g. if user deleted intersection)
81090 function continueTo(nextStep) {
81091 context.map().on('move.intro drawn.intro', null);
81092 context.history().on('change.intro', null);
81097 function retrySplit() {
81098 context.enter(modeBrowse(context));
81099 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
81101 var advance = function advance() {
81102 continueTo(rightClickIntersection);
81105 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81106 var box = pad(eleventhAvenueEnd, padding, context);
81107 reveal(box, helpHtml('intro.lines.retry_split'), {
81108 buttonText: _t.html('intro.ok'),
81109 buttonCallback: advance
81111 context.map().on('move.intro drawn.intro', function () {
81112 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81113 var box = pad(eleventhAvenueEnd, padding, context);
81114 reveal(box, helpHtml('intro.lines.retry_split'), {
81116 buttonText: _t.html('intro.ok'),
81117 buttonCallback: advance
81121 function continueTo(nextStep) {
81122 context.map().on('move.intro drawn.intro', null);
81127 function didSplit() {
81128 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81129 return continueTo(rightClickIntersection);
81132 var ids = context.selectedIDs();
81133 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
81134 var street = _t('intro.graph.name.washington-street');
81135 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81136 var box = pad(twelfthAvenue, padding, context);
81137 box.width = box.width / 2;
81138 reveal(box, helpHtml(string, {
81144 timeout(function () {
81145 context.map().centerZoomEase(twelfthAvenue, 18, 500);
81146 context.map().on('move.intro drawn.intro', function () {
81147 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81148 var box = pad(twelfthAvenue, padding, context);
81149 box.width = box.width / 2;
81150 reveal(box, helpHtml(string, {
81157 }, 600); // after initial reveal and curtain cut
81159 context.on('enter.intro', function () {
81160 var ids = context.selectedIDs();
81162 if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
81163 continueTo(multiSelect);
81166 context.history().on('change.intro', function () {
81167 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81168 return continueTo(rightClickIntersection);
81172 function continueTo(nextStep) {
81173 context.map().on('move.intro drawn.intro', null);
81174 context.on('enter.intro', null);
81175 context.history().on('change.intro', null);
81180 function multiSelect() {
81181 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81182 return continueTo(rightClickIntersection);
81185 var ids = context.selectedIDs();
81186 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
81187 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
81189 if (hasWashington && hasTwelfth) {
81190 return continueTo(multiRightClick);
81191 } else if (!hasWashington && !hasTwelfth) {
81192 return continueTo(didSplit);
81195 context.map().centerZoomEase(twelfthAvenue, 18, 500);
81196 timeout(function () {
81197 var selected, other, padding, box;
81199 if (hasWashington) {
81200 selected = _t('intro.graph.name.washington-street');
81201 other = _t('intro.graph.name.12th-avenue');
81202 padding = 60 * Math.pow(2, context.map().zoom() - 18);
81203 box = pad(twelfthAvenueEnd, padding, context);
81206 selected = _t('intro.graph.name.12th-avenue');
81207 other = _t('intro.graph.name.washington-street');
81208 padding = 200 * Math.pow(2, context.map().zoom() - 18);
81209 box = pad(twelfthAvenue, padding, context);
81213 reveal(box, helpHtml('intro.lines.multi_select', {
81214 selected: selected,
81216 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
81217 selected: selected,
81220 context.map().on('move.intro drawn.intro', function () {
81221 if (hasWashington) {
81222 selected = _t('intro.graph.name.washington-street');
81223 other = _t('intro.graph.name.12th-avenue');
81224 padding = 60 * Math.pow(2, context.map().zoom() - 18);
81225 box = pad(twelfthAvenueEnd, padding, context);
81228 selected = _t('intro.graph.name.12th-avenue');
81229 other = _t('intro.graph.name.washington-street');
81230 padding = 200 * Math.pow(2, context.map().zoom() - 18);
81231 box = pad(twelfthAvenue, padding, context);
81235 reveal(box, helpHtml('intro.lines.multi_select', {
81236 selected: selected,
81238 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
81239 selected: selected,
81245 context.on('enter.intro', function () {
81246 continueTo(multiSelect);
81248 context.history().on('change.intro', function () {
81249 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81250 return continueTo(rightClickIntersection);
81255 function continueTo(nextStep) {
81256 context.map().on('move.intro drawn.intro', null);
81257 context.on('enter.intro', null);
81258 context.history().on('change.intro', null);
81263 function multiRightClick() {
81264 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81265 return continueTo(rightClickIntersection);
81268 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81269 var box = pad(twelfthAvenue, padding, context);
81270 var rightClickString = helpHtml('intro.lines.multi_select_success') + helpHtml('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
81271 reveal(box, rightClickString);
81272 context.map().on('move.intro drawn.intro', function () {
81273 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81274 var box = pad(twelfthAvenue, padding, context);
81275 reveal(box, rightClickString, {
81279 context.ui().editMenu().on('toggled.intro', function (open) {
81281 timeout(function () {
81282 var ids = context.selectedIDs();
81284 if (ids.length === 2 && ids.indexOf(twelfthAvenueID) !== -1 && ids.indexOf(_washingtonSegmentID) !== -1) {
81285 var node = selectMenuItem(context, 'delete').node();
81287 continueTo(multiDelete);
81288 } else if (ids.length === 1 && ids.indexOf(_washingtonSegmentID) !== -1) {
81289 return continueTo(multiSelect);
81291 return continueTo(didSplit);
81293 }, 300); // after edit menu visible
81295 context.history().on('change.intro', function () {
81296 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81297 return continueTo(rightClickIntersection);
81301 function continueTo(nextStep) {
81302 context.map().on('move.intro drawn.intro', null);
81303 context.ui().editMenu().on('toggled.intro', null);
81304 context.history().on('change.intro', null);
81309 function multiDelete() {
81310 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81311 return continueTo(rightClickIntersection);
81314 var node = selectMenuItem(context, 'delete').node();
81315 if (!node) return continueTo(multiRightClick);
81316 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
81319 context.map().on('move.intro drawn.intro', function () {
81320 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
81325 context.on('exit.intro', function () {
81326 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
81327 return continueTo(multiSelect); // left select mode but roads still exist
81330 context.history().on('change.intro', function () {
81331 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
81332 continueTo(retryDelete); // changed something but roads still exist
81338 function continueTo(nextStep) {
81339 context.map().on('move.intro drawn.intro', null);
81340 context.on('exit.intro', null);
81341 context.history().on('change.intro', null);
81346 function retryDelete() {
81347 context.enter(modeBrowse(context));
81348 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81349 var box = pad(twelfthAvenue, padding, context);
81350 reveal(box, helpHtml('intro.lines.retry_delete'), {
81351 buttonText: _t.html('intro.ok'),
81352 buttonCallback: function buttonCallback() {
81353 continueTo(multiSelect);
81357 function continueTo(nextStep) {
81363 dispatch$1.call('done');
81364 reveal('.ideditor', helpHtml('intro.lines.play', {
81365 next: _t('intro.buildings.title')
81367 tooltipBox: '.intro-nav-wrap .chapter-building',
81368 buttonText: _t.html('intro.ok'),
81369 buttonCallback: function buttonCallback() {
81370 reveal('.ideditor');
81375 chapter.enter = function () {
81379 chapter.exit = function () {
81380 timeouts.forEach(window.clearTimeout);
81381 select(window).on('pointerdown.intro mousedown.intro', null, true);
81382 context.on('enter.intro exit.intro', null);
81383 context.map().on('move.intro drawn.intro', null);
81384 context.history().on('change.intro', null);
81385 context.container().select('.inspector-wrap').on('wheel.intro', null);
81386 context.container().select('.preset-list-button').on('click.intro', null);
81389 chapter.restart = function () {
81394 return utilRebind(chapter, dispatch$1, 'on');
81397 function uiIntroBuilding(context, reveal) {
81398 var dispatch$1 = dispatch('done');
81399 var house = [-85.62815, 41.95638];
81400 var tank = [-85.62732, 41.95347];
81401 var buildingCatetory = _mainPresetIndex.item('category-building');
81402 var housePreset = _mainPresetIndex.item('building/house');
81403 var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
81405 var _houseID = null;
81406 var _tankID = null;
81408 title: 'intro.buildings.title'
81411 function timeout(f, t) {
81412 timeouts.push(window.setTimeout(f, t));
81415 function eventCancel(d3_event) {
81416 d3_event.stopPropagation();
81417 d3_event.preventDefault();
81420 function revealHouse(center, text, options) {
81421 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
81422 var box = pad(center, padding, context);
81423 reveal(box, text, options);
81426 function revealTank(center, text, options) {
81427 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
81428 var box = pad(center, padding, context);
81429 reveal(box, text, options);
81432 function addHouse() {
81433 context.enter(modeBrowse(context));
81434 context.history().reset('initial');
81436 var msec = transitionTime(house, context.map().center());
81439 reveal(null, null, {
81444 context.map().centerZoomEase(house, 19, msec);
81445 timeout(function () {
81446 var tooltip = reveal('button.add-area', helpHtml('intro.buildings.add_building'));
81447 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-buildings');
81448 context.on('enter.intro', function (mode) {
81449 if (mode.id !== 'add-area') return;
81450 continueTo(startHouse);
81454 function continueTo(nextStep) {
81455 context.on('enter.intro', null);
81460 function startHouse() {
81461 if (context.mode().id !== 'add-area') {
81462 return continueTo(addHouse);
81466 context.map().zoomEase(20, 500);
81467 timeout(function () {
81468 var startString = helpHtml('intro.buildings.start_building') + helpHtml('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
81469 revealHouse(house, startString);
81470 context.map().on('move.intro drawn.intro', function () {
81471 revealHouse(house, startString, {
81475 context.on('enter.intro', function (mode) {
81476 if (mode.id !== 'draw-area') return chapter.restart();
81477 continueTo(continueHouse);
81479 }, 550); // after easing
81481 function continueTo(nextStep) {
81482 context.map().on('move.intro drawn.intro', null);
81483 context.on('enter.intro', null);
81488 function continueHouse() {
81489 if (context.mode().id !== 'draw-area') {
81490 return continueTo(addHouse);
81494 var continueString = helpHtml('intro.buildings.continue_building') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_building');
81495 revealHouse(house, continueString);
81496 context.map().on('move.intro drawn.intro', function () {
81497 revealHouse(house, continueString, {
81501 context.on('enter.intro', function (mode) {
81502 if (mode.id === 'draw-area') {
81504 } else if (mode.id === 'select') {
81505 var graph = context.graph();
81506 var way = context.entity(context.selectedIDs()[0]);
81507 var nodes = graph.childNodes(way);
81508 var points = utilArrayUniq(nodes).map(function (n) {
81509 return context.projection(n.loc);
81512 if (isMostlySquare(points)) {
81514 return continueTo(chooseCategoryBuilding);
81516 return continueTo(retryHouse);
81519 return chapter.restart();
81523 function continueTo(nextStep) {
81524 context.map().on('move.intro drawn.intro', null);
81525 context.on('enter.intro', null);
81530 function retryHouse() {
81531 var onClick = function onClick() {
81532 continueTo(addHouse);
81535 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
81536 buttonText: _t.html('intro.ok'),
81537 buttonCallback: onClick
81539 context.map().on('move.intro drawn.intro', function () {
81540 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
81542 buttonText: _t.html('intro.ok'),
81543 buttonCallback: onClick
81547 function continueTo(nextStep) {
81548 context.map().on('move.intro drawn.intro', null);
81553 function chooseCategoryBuilding() {
81554 if (!_houseID || !context.hasEntity(_houseID)) {
81558 var ids = context.selectedIDs();
81560 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81561 context.enter(modeSelect(context, [_houseID]));
81562 } // disallow scrolling
81565 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81566 timeout(function () {
81567 // reset pane, in case user somehow happened to change it..
81568 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
81569 var button = context.container().select('.preset-category-building .preset-list-button');
81570 reveal(button.node(), helpHtml('intro.buildings.choose_category_building', {
81571 category: buildingCatetory.name()
81573 button.on('click.intro', function () {
81574 button.on('click.intro', null);
81575 continueTo(choosePresetHouse);
81577 }, 400); // after preset list pane visible..
81579 context.on('enter.intro', function (mode) {
81580 if (!_houseID || !context.hasEntity(_houseID)) {
81581 return continueTo(addHouse);
81584 var ids = context.selectedIDs();
81586 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
81587 return continueTo(chooseCategoryBuilding);
81591 function continueTo(nextStep) {
81592 context.container().select('.inspector-wrap').on('wheel.intro', null);
81593 context.container().select('.preset-list-button').on('click.intro', null);
81594 context.on('enter.intro', null);
81599 function choosePresetHouse() {
81600 if (!_houseID || !context.hasEntity(_houseID)) {
81604 var ids = context.selectedIDs();
81606 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81607 context.enter(modeSelect(context, [_houseID]));
81608 } // disallow scrolling
81611 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81612 timeout(function () {
81613 // reset pane, in case user somehow happened to change it..
81614 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
81615 var button = context.container().select('.preset-building-house .preset-list-button');
81616 reveal(button.node(), helpHtml('intro.buildings.choose_preset_house', {
81617 preset: housePreset.name()
81621 button.on('click.intro', function () {
81622 button.on('click.intro', null);
81623 continueTo(closeEditorHouse);
81625 }, 400); // after preset list pane visible..
81627 context.on('enter.intro', function (mode) {
81628 if (!_houseID || !context.hasEntity(_houseID)) {
81629 return continueTo(addHouse);
81632 var ids = context.selectedIDs();
81634 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
81635 return continueTo(chooseCategoryBuilding);
81639 function continueTo(nextStep) {
81640 context.container().select('.inspector-wrap').on('wheel.intro', null);
81641 context.container().select('.preset-list-button').on('click.intro', null);
81642 context.on('enter.intro', null);
81647 function closeEditorHouse() {
81648 if (!_houseID || !context.hasEntity(_houseID)) {
81652 var ids = context.selectedIDs();
81654 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81655 context.enter(modeSelect(context, [_houseID]));
81658 context.history().checkpoint('hasHouse');
81659 context.on('exit.intro', function () {
81660 continueTo(rightClickHouse);
81662 timeout(function () {
81663 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
81664 button: icon('#iD-icon-close', 'inline')
81668 function continueTo(nextStep) {
81669 context.on('exit.intro', null);
81674 function rightClickHouse() {
81675 if (!_houseID) return chapter.restart();
81676 context.enter(modeBrowse(context));
81677 context.history().reset('hasHouse');
81678 var zoom = context.map().zoom();
81684 context.map().centerZoomEase(house, zoom, 500);
81685 context.on('enter.intro', function (mode) {
81686 if (mode.id !== 'select') return;
81687 var ids = context.selectedIDs();
81688 if (ids.length !== 1 || ids[0] !== _houseID) return;
81689 timeout(function () {
81690 var node = selectMenuItem(context, 'orthogonalize').node();
81692 continueTo(clickSquare);
81693 }, 50); // after menu visible
81695 context.map().on('move.intro drawn.intro', function () {
81696 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
81697 revealHouse(house, rightclickString, {
81701 context.history().on('change.intro', function () {
81702 continueTo(rightClickHouse);
81705 function continueTo(nextStep) {
81706 context.on('enter.intro', null);
81707 context.map().on('move.intro drawn.intro', null);
81708 context.history().on('change.intro', null);
81713 function clickSquare() {
81714 if (!_houseID) return chapter.restart();
81715 var entity = context.hasEntity(_houseID);
81716 if (!entity) return continueTo(rightClickHouse);
81717 var node = selectMenuItem(context, 'orthogonalize').node();
81720 return continueTo(rightClickHouse);
81723 var wasChanged = false;
81724 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
81727 context.on('enter.intro', function (mode) {
81728 if (mode.id === 'browse') {
81729 continueTo(rightClickHouse);
81730 } else if (mode.id === 'move' || mode.id === 'rotate') {
81731 continueTo(retryClickSquare);
81734 context.map().on('move.intro', function () {
81735 var node = selectMenuItem(context, 'orthogonalize').node();
81737 if (!wasChanged && !node) {
81738 return continueTo(rightClickHouse);
81741 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
81746 context.history().on('change.intro', function () {
81748 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
81750 timeout(function () {
81751 if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature', {
81754 continueTo(doneSquare);
81756 continueTo(retryClickSquare);
81758 }, 500); // after transitioned actions
81761 function continueTo(nextStep) {
81762 context.on('enter.intro', null);
81763 context.map().on('move.intro', null);
81764 context.history().on('change.intro', null);
81769 function retryClickSquare() {
81770 context.enter(modeBrowse(context));
81771 revealHouse(house, helpHtml('intro.buildings.retry_square'), {
81772 buttonText: _t.html('intro.ok'),
81773 buttonCallback: function buttonCallback() {
81774 continueTo(rightClickHouse);
81778 function continueTo(nextStep) {
81783 function doneSquare() {
81784 context.history().checkpoint('doneSquare');
81785 revealHouse(house, helpHtml('intro.buildings.done_square'), {
81786 buttonText: _t.html('intro.ok'),
81787 buttonCallback: function buttonCallback() {
81788 continueTo(addTank);
81792 function continueTo(nextStep) {
81797 function addTank() {
81798 context.enter(modeBrowse(context));
81799 context.history().reset('doneSquare');
81801 var msec = transitionTime(tank, context.map().center());
81804 reveal(null, null, {
81809 context.map().centerZoomEase(tank, 19.5, msec);
81810 timeout(function () {
81811 reveal('button.add-area', helpHtml('intro.buildings.add_tank'));
81812 context.on('enter.intro', function (mode) {
81813 if (mode.id !== 'add-area') return;
81814 continueTo(startTank);
81818 function continueTo(nextStep) {
81819 context.on('enter.intro', null);
81824 function startTank() {
81825 if (context.mode().id !== 'add-area') {
81826 return continueTo(addTank);
81830 timeout(function () {
81831 var startString = helpHtml('intro.buildings.start_tank') + helpHtml('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
81832 revealTank(tank, startString);
81833 context.map().on('move.intro drawn.intro', function () {
81834 revealTank(tank, startString, {
81838 context.on('enter.intro', function (mode) {
81839 if (mode.id !== 'draw-area') return chapter.restart();
81840 continueTo(continueTank);
81842 }, 550); // after easing
81844 function continueTo(nextStep) {
81845 context.map().on('move.intro drawn.intro', null);
81846 context.on('enter.intro', null);
81851 function continueTank() {
81852 if (context.mode().id !== 'draw-area') {
81853 return continueTo(addTank);
81857 var continueString = helpHtml('intro.buildings.continue_tank') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_tank');
81858 revealTank(tank, continueString);
81859 context.map().on('move.intro drawn.intro', function () {
81860 revealTank(tank, continueString, {
81864 context.on('enter.intro', function (mode) {
81865 if (mode.id === 'draw-area') {
81867 } else if (mode.id === 'select') {
81868 _tankID = context.selectedIDs()[0];
81869 return continueTo(searchPresetTank);
81871 return continueTo(addTank);
81875 function continueTo(nextStep) {
81876 context.map().on('move.intro drawn.intro', null);
81877 context.on('enter.intro', null);
81882 function searchPresetTank() {
81883 if (!_tankID || !context.hasEntity(_tankID)) {
81887 var ids = context.selectedIDs();
81889 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
81890 context.enter(modeSelect(context, [_tankID]));
81891 } // disallow scrolling
81894 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81895 timeout(function () {
81896 // reset pane, in case user somehow happened to change it..
81897 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
81898 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
81899 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
81900 preset: tankPreset.name()
81902 }, 400); // after preset list pane visible..
81904 context.on('enter.intro', function (mode) {
81905 if (!_tankID || !context.hasEntity(_tankID)) {
81906 return continueTo(addTank);
81909 var ids = context.selectedIDs();
81911 if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
81912 // keep the user's area selected..
81913 context.enter(modeSelect(context, [_tankID])); // reset pane, in case user somehow happened to change it..
81915 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
81917 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81918 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
81919 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
81920 preset: tankPreset.name()
81922 context.history().on('change.intro', null);
81926 function checkPresetSearch() {
81927 var first = context.container().select('.preset-list-item:first-child');
81929 if (first.classed('preset-man_made-storage_tank')) {
81930 reveal(first.select('.preset-list-button').node(), helpHtml('intro.buildings.choose_tank', {
81931 preset: tankPreset.name()
81935 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
81936 context.history().on('change.intro', function () {
81937 continueTo(closeEditorTank);
81942 function continueTo(nextStep) {
81943 context.container().select('.inspector-wrap').on('wheel.intro', null);
81944 context.on('enter.intro', null);
81945 context.history().on('change.intro', null);
81946 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
81951 function closeEditorTank() {
81952 if (!_tankID || !context.hasEntity(_tankID)) {
81956 var ids = context.selectedIDs();
81958 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
81959 context.enter(modeSelect(context, [_tankID]));
81962 context.history().checkpoint('hasTank');
81963 context.on('exit.intro', function () {
81964 continueTo(rightClickTank);
81966 timeout(function () {
81967 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
81968 button: icon('#iD-icon-close', 'inline')
81972 function continueTo(nextStep) {
81973 context.on('exit.intro', null);
81978 function rightClickTank() {
81979 if (!_tankID) return continueTo(addTank);
81980 context.enter(modeBrowse(context));
81981 context.history().reset('hasTank');
81982 context.map().centerEase(tank, 500);
81983 timeout(function () {
81984 context.on('enter.intro', function (mode) {
81985 if (mode.id !== 'select') return;
81986 var ids = context.selectedIDs();
81987 if (ids.length !== 1 || ids[0] !== _tankID) return;
81988 timeout(function () {
81989 var node = selectMenuItem(context, 'circularize').node();
81991 continueTo(clickCircle);
81992 }, 50); // after menu visible
81994 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
81995 revealTank(tank, rightclickString);
81996 context.map().on('move.intro drawn.intro', function () {
81997 revealTank(tank, rightclickString, {
82001 context.history().on('change.intro', function () {
82002 continueTo(rightClickTank);
82006 function continueTo(nextStep) {
82007 context.on('enter.intro', null);
82008 context.map().on('move.intro drawn.intro', null);
82009 context.history().on('change.intro', null);
82014 function clickCircle() {
82015 if (!_tankID) return chapter.restart();
82016 var entity = context.hasEntity(_tankID);
82017 if (!entity) return continueTo(rightClickTank);
82018 var node = selectMenuItem(context, 'circularize').node();
82021 return continueTo(rightClickTank);
82024 var wasChanged = false;
82025 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
82028 context.on('enter.intro', function (mode) {
82029 if (mode.id === 'browse') {
82030 continueTo(rightClickTank);
82031 } else if (mode.id === 'move' || mode.id === 'rotate') {
82032 continueTo(retryClickCircle);
82035 context.map().on('move.intro', function () {
82036 var node = selectMenuItem(context, 'circularize').node();
82038 if (!wasChanged && !node) {
82039 return continueTo(rightClickTank);
82042 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
82047 context.history().on('change.intro', function () {
82049 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
82051 timeout(function () {
82052 if (context.history().undoAnnotation() === _t('operations.circularize.annotation.feature', {
82057 continueTo(retryClickCircle);
82059 }, 500); // after transitioned actions
82062 function continueTo(nextStep) {
82063 context.on('enter.intro', null);
82064 context.map().on('move.intro', null);
82065 context.history().on('change.intro', null);
82070 function retryClickCircle() {
82071 context.enter(modeBrowse(context));
82072 revealTank(tank, helpHtml('intro.buildings.retry_circle'), {
82073 buttonText: _t.html('intro.ok'),
82074 buttonCallback: function buttonCallback() {
82075 continueTo(rightClickTank);
82079 function continueTo(nextStep) {
82085 dispatch$1.call('done');
82086 reveal('.ideditor', helpHtml('intro.buildings.play', {
82087 next: _t('intro.startediting.title')
82089 tooltipBox: '.intro-nav-wrap .chapter-startEditing',
82090 buttonText: _t.html('intro.ok'),
82091 buttonCallback: function buttonCallback() {
82092 reveal('.ideditor');
82097 chapter.enter = function () {
82101 chapter.exit = function () {
82102 timeouts.forEach(window.clearTimeout);
82103 context.on('enter.intro exit.intro', null);
82104 context.map().on('move.intro drawn.intro', null);
82105 context.history().on('change.intro', null);
82106 context.container().select('.inspector-wrap').on('wheel.intro', null);
82107 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
82108 context.container().select('.more-fields .combobox-input').on('click.intro', null);
82111 chapter.restart = function () {
82116 return utilRebind(chapter, dispatch$1, 'on');
82119 function uiIntroStartEditing(context, reveal) {
82120 var dispatch$1 = dispatch('done', 'startEditing');
82121 var modalSelection = select(null);
82123 title: 'intro.startediting.title'
82126 function showHelp() {
82127 reveal('.map-control.help-control', helpHtml('intro.startediting.help'), {
82128 buttonText: _t.html('intro.ok'),
82129 buttonCallback: function buttonCallback() {
82135 function shortcuts() {
82136 reveal('.map-control.help-control', helpHtml('intro.startediting.shortcuts'), {
82137 buttonText: _t.html('intro.ok'),
82138 buttonCallback: function buttonCallback() {
82144 function showSave() {
82145 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82147 reveal('.top-toolbar button.save', helpHtml('intro.startediting.save'), {
82148 buttonText: _t.html('intro.ok'),
82149 buttonCallback: function buttonCallback() {
82155 function showStart() {
82156 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82158 modalSelection = uiModal(context.container());
82159 modalSelection.select('.modal').attr('class', 'modal-splash modal');
82160 modalSelection.selectAll('.close').remove();
82161 var startbutton = modalSelection.select('.content').attr('class', 'fillL').append('button').attr('class', 'modal-section huge-modal-button').on('click', function () {
82162 modalSelection.remove();
82164 startbutton.append('svg').attr('class', 'illustration').append('use').attr('xlink:href', '#iD-logo-walkthrough');
82165 startbutton.append('h2').html(_t.html('intro.startediting.start'));
82166 dispatch$1.call('startEditing');
82169 chapter.enter = function () {
82173 chapter.exit = function () {
82174 modalSelection.remove();
82175 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82178 return utilRebind(chapter, dispatch$1, 'on');
82182 welcome: uiIntroWelcome,
82183 navigation: uiIntroNavigation,
82184 point: uiIntroPoint,
82187 building: uiIntroBuilding,
82188 startEditing: uiIntroStartEditing
82190 var chapterFlow = ['welcome', 'navigation', 'point', 'area', 'line', 'building', 'startEditing'];
82191 function uiIntro(context) {
82192 var INTRO_IMAGERY = 'EsriWorldImageryClarity';
82193 var _introGraph = {};
82197 function intro(selection) {
82198 _mainFileFetcher.get('intro_graph').then(function (dataIntroGraph) {
82199 // create entities for intro graph and localize names
82200 for (var id in dataIntroGraph) {
82201 if (!_introGraph[id]) {
82202 _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
82206 selection.call(startIntro);
82207 })["catch"](function () {
82212 function startIntro(selection) {
82213 context.enter(modeBrowse(context)); // Save current map state
82215 var osm = context.connection();
82216 var history = context.history().toJSON();
82217 var hash = window.location.hash;
82218 var center = context.map().center();
82219 var zoom = context.map().zoom();
82220 var background = context.background().baseLayerSource();
82221 var overlays = context.background().overlayLayerSources();
82222 var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
82223 var caches = osm && osm.caches();
82224 var baseEntities = context.history().graph().base().entities; // Show sidebar and disable the sidebar resizing button
82225 // (this needs to be before `context.inIntro(true)`)
82227 context.ui().sidebar.expand();
82228 context.container().selectAll('button.sidebar-toggle').classed('disabled', true); // Block saving
82230 context.inIntro(true); // Load semi-real data used in intro
82233 osm.toggle(false).reset();
82236 context.history().reset();
82237 context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
82238 context.history().checkpoint('initial'); // Setup imagery
82240 var imagery = context.background().findSource(INTRO_IMAGERY);
82243 context.background().baseLayerSource(imagery);
82245 context.background().bing();
82248 overlays.forEach(function (d) {
82249 return context.background().toggleOverlayLayer(d);
82250 }); // Setup data layers (only OSM)
82252 var layers = context.layers();
82253 layers.all().forEach(function (item) {
82254 // if the layer has the function `enabled`
82255 if (typeof item.layer.enabled === 'function') {
82256 item.layer.enabled(item.id === 'osm');
82259 context.container().selectAll('.main-map .layer-background').style('opacity', 1);
82260 var curtain = uiCurtain(context.container().node());
82261 selection.call(curtain); // Store that the user started the walkthrough..
82263 corePreferences('walkthrough_started', 'yes'); // Restore previous walkthrough progress..
82265 var storedProgress = corePreferences('walkthrough_progress') || '';
82266 var progress = storedProgress.split(';').filter(Boolean);
82267 var chapters = chapterFlow.map(function (chapter, i) {
82268 var s = chapterUi[chapter](context, curtain.reveal).on('done', function () {
82269 buttons.filter(function (d) {
82270 return d.title === s.title;
82271 }).classed('finished', true);
82273 if (i < chapterFlow.length - 1) {
82274 var next = chapterFlow[i + 1];
82275 context.container().select("button.chapter-".concat(next)).classed('next', true);
82276 } // Store walkthrough progress..
82279 progress.push(chapter);
82280 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
82284 chapters[chapters.length - 1].on('startEditing', function () {
82285 // Store walkthrough progress..
82286 progress.push('startEditing');
82287 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); // Store if walkthrough is completed..
82289 var incomplete = utilArrayDifference(chapterFlow, progress);
82291 if (!incomplete.length) {
82292 corePreferences('walkthrough_completed', 'yes');
82297 context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
82298 context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
82301 osm.toggle(true).reset().caches(caches);
82304 context.history().reset().merge(Object.values(baseEntities));
82305 context.background().baseLayerSource(background);
82306 overlays.forEach(function (d) {
82307 return context.background().toggleOverlayLayer(d);
82311 context.history().fromJSON(history, false);
82314 context.map().centerZoom(center, zoom);
82315 window.location.replace(hash);
82316 context.inIntro(false);
82318 var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
82319 navwrap.append('svg').attr('class', 'intro-nav-wrap-logo').append('use').attr('xlink:href', '#iD-logo-walkthrough');
82320 var buttonwrap = navwrap.append('div').attr('class', 'joined').selectAll('button.chapter');
82321 var buttons = buttonwrap.data(chapters).enter().append('button').attr('class', function (d, i) {
82322 return "chapter chapter-".concat(chapterFlow[i]);
82323 }).on('click', enterChapter);
82324 buttons.append('span').html(function (d) {
82325 return _t.html(d.title);
82327 buttons.append('span').attr('class', 'status').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
82328 enterChapter(null, chapters[0]);
82330 function enterChapter(d3_event, newChapter) {
82331 if (_currChapter) {
82332 _currChapter.exit();
82335 context.enter(modeBrowse(context));
82336 _currChapter = newChapter;
82338 _currChapter.enter();
82340 buttons.classed('next', false).classed('active', function (d) {
82341 return d.title === _currChapter.title;
82349 function uiIssuesInfo(context) {
82350 var warningsItem = {
82353 iconID: 'iD-icon-alert',
82354 descriptionID: 'issues.warnings_and_errors'
82356 var resolvedItem = {
82359 iconID: 'iD-icon-apply',
82360 descriptionID: 'issues.user_resolved_issues'
82363 function update(selection) {
82364 var shownItems = [];
82365 var liveIssues = context.validator().getIssues({
82366 what: corePreferences('validate-what') || 'edited',
82367 where: corePreferences('validate-where') || 'all'
82370 if (liveIssues.length) {
82371 warningsItem.count = liveIssues.length;
82372 shownItems.push(warningsItem);
82375 if (corePreferences('validate-what') === 'all') {
82376 var resolvedIssues = context.validator().getResolvedIssues();
82378 if (resolvedIssues.length) {
82379 resolvedItem.count = resolvedIssues.length;
82380 shownItems.push(resolvedItem);
82384 var chips = selection.selectAll('.chip').data(shownItems, function (d) {
82387 chips.exit().remove();
82388 var enter = chips.enter().append('a').attr('class', function (d) {
82389 return 'chip ' + d.id + '-count';
82390 }).attr('href', '#').each(function (d) {
82391 var chipSelection = select(this);
82392 var tooltipBehavior = uiTooltip().placement('top').title(_t.html(d.descriptionID));
82393 chipSelection.call(tooltipBehavior).on('click', function (d3_event) {
82394 d3_event.preventDefault();
82395 tooltipBehavior.hide(select(this)); // open the Issues pane
82397 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
82399 chipSelection.call(svgIcon('#' + d.iconID));
82401 enter.append('span').attr('class', 'count');
82402 enter.merge(chips).selectAll('span.count').html(function (d) {
82403 return d.count.toString();
82407 return function (selection) {
82409 context.validator().on('validated.infobox', function () {
82415 function uiMapInMap(context) {
82416 function mapInMap(selection) {
82417 var backgroundLayer = rendererTileLayer(context);
82418 var overlayLayers = {};
82419 var projection = geoRawMercator();
82420 var dataLayer = svgData(projection, context).showLabels(false);
82421 var debugLayer = svgDebug(projection, context);
82422 var zoom = d3_zoom().scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]).on('start', zoomStarted).on('zoom', zoomed).on('end', zoomEnded);
82423 var wrap = select(null);
82424 var tiles = select(null);
82425 var viewport = select(null);
82426 var _isTransformed = false;
82427 var _isHidden = true;
82428 var _skipEvents = false;
82429 var _gesture = null;
82430 var _zDiff = 6; // by default, minimap renders at (main zoom - 6)
82432 var _dMini; // dimensions of minimap
82435 var _cMini; // center pixel of minimap
82438 var _tStart; // transform at start of gesture
82441 var _tCurr; // transform at most recent event
82446 function zoomStarted() {
82447 if (_skipEvents) return;
82448 _tStart = _tCurr = projection.transform();
82452 function zoomed(d3_event) {
82453 if (_skipEvents) return;
82454 var x = d3_event.transform.x;
82455 var y = d3_event.transform.y;
82456 var k = d3_event.transform.k;
82457 var isZooming = k !== _tStart.k;
82458 var isPanning = x !== _tStart.x || y !== _tStart.y;
82460 if (!isZooming && !isPanning) {
82461 return; // no change
82462 } // lock in either zooming or panning, don't allow both in minimap.
82466 _gesture = isZooming ? 'zoom' : 'pan';
82469 var tMini = projection.transform();
82472 if (_gesture === 'zoom') {
82473 scale = k / tMini.k;
82474 tX = (_cMini[0] / scale - _cMini[0]) * scale;
82475 tY = (_cMini[1] / scale - _cMini[1]) * scale;
82483 utilSetTransform(tiles, tX, tY, scale);
82484 utilSetTransform(viewport, 0, 0, scale);
82485 _isTransformed = true;
82486 _tCurr = identity$2.translate(x, y).scale(k);
82487 var zMain = geoScaleToZoom(context.projection.scale());
82488 var zMini = geoScaleToZoom(k);
82489 _zDiff = zMain - zMini;
82493 function zoomEnded() {
82494 if (_skipEvents) return;
82495 if (_gesture !== 'pan') return;
82496 updateProjection();
82498 context.map().center(projection.invert(_cMini)); // recenter main map..
82501 function updateProjection() {
82502 var loc = context.map().center();
82503 var tMain = context.projection.transform();
82504 var zMain = geoScaleToZoom(tMain.k);
82505 var zMini = Math.max(zMain - _zDiff, 0.5);
82506 var kMini = geoZoomToScale(zMini);
82507 projection.translate([tMain.x, tMain.y]).scale(kMini);
82508 var point = projection(loc);
82509 var mouse = _gesture === 'pan' ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
82510 var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
82511 var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
82512 projection.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]);
82513 _tCurr = projection.transform();
82515 if (_isTransformed) {
82516 utilSetTransform(tiles, 0, 0);
82517 utilSetTransform(viewport, 0, 0);
82518 _isTransformed = false;
82521 zoom.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
82522 _skipEvents = true;
82523 wrap.call(zoom.transform, _tCurr);
82524 _skipEvents = false;
82527 function redraw() {
82528 clearTimeout(_timeoutID);
82529 if (_isHidden) return;
82530 updateProjection();
82531 var zMini = geoScaleToZoom(projection.scale()); // setup tile container
82533 tiles = wrap.selectAll('.map-in-map-tiles').data([0]);
82534 tiles = tiles.enter().append('div').attr('class', 'map-in-map-tiles').merge(tiles); // redraw background
82536 backgroundLayer.source(context.background().baseLayerSource()).projection(projection).dimensions(_dMini);
82537 var background = tiles.selectAll('.map-in-map-background').data([0]);
82538 background.enter().append('div').attr('class', 'map-in-map-background').merge(background).call(backgroundLayer); // redraw overlay
82540 var overlaySources = context.background().overlayLayerSources();
82541 var activeOverlayLayers = [];
82543 for (var i = 0; i < overlaySources.length; i++) {
82544 if (overlaySources[i].validZoom(zMini)) {
82545 if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
82546 activeOverlayLayers.push(overlayLayers[i].source(overlaySources[i]).projection(projection).dimensions(_dMini));
82550 var overlay = tiles.selectAll('.map-in-map-overlay').data([0]);
82551 overlay = overlay.enter().append('div').attr('class', 'map-in-map-overlay').merge(overlay);
82552 var overlays = overlay.selectAll('div').data(activeOverlayLayers, function (d) {
82553 return d.source().name();
82555 overlays.exit().remove();
82556 overlays = overlays.enter().append('div').merge(overlays).each(function (layer) {
82557 select(this).call(layer);
82559 var dataLayers = tiles.selectAll('.map-in-map-data').data([0]);
82560 dataLayers.exit().remove();
82561 dataLayers = dataLayers.enter().append('svg').attr('class', 'map-in-map-data').merge(dataLayers).call(dataLayer).call(debugLayer); // redraw viewport bounding box
82563 if (_gesture !== 'pan') {
82564 var getPath = d3_geoPath(projection);
82567 coordinates: [context.map().extent().polygon()]
82569 viewport = wrap.selectAll('.map-in-map-viewport').data([0]);
82570 viewport = viewport.enter().append('svg').attr('class', 'map-in-map-viewport').merge(viewport);
82571 var path = viewport.selectAll('.map-in-map-bbox').data([bbox]);
82572 path.enter().append('path').attr('class', 'map-in-map-bbox').merge(path).attr('d', getPath).classed('thick', function (d) {
82573 return getPath.area(d) < 30;
82578 function queueRedraw() {
82579 clearTimeout(_timeoutID);
82580 _timeoutID = setTimeout(function () {
82585 function toggle(d3_event) {
82586 if (d3_event) d3_event.preventDefault();
82587 _isHidden = !_isHidden;
82588 context.container().select('.minimap-toggle-item').classed('active', !_isHidden).select('input').property('checked', !_isHidden);
82591 wrap.style('display', 'block').style('opacity', '1').transition().duration(200).style('opacity', '0').on('end', function () {
82592 selection.selectAll('.map-in-map').style('display', 'none');
82595 wrap.style('display', 'block').style('opacity', '0').transition().duration(200).style('opacity', '1').on('end', function () {
82601 uiMapInMap.toggle = toggle;
82602 wrap = selection.selectAll('.map-in-map').data([0]);
82603 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..
82605 _dMini = [200, 150]; //utilGetDimensions(wrap);
82607 _cMini = geoVecScale(_dMini, 0.5);
82608 context.map().on('drawn.map-in-map', function (drawn) {
82609 if (drawn.full === true) {
82614 context.keybinding().on(_t('background.minimap.key'), toggle);
82620 function uiNotice(context) {
82621 return function (selection) {
82622 var div = selection.append('div').attr('class', 'notice');
82623 var button = div.append('button').attr('class', 'zoom-to notice fillD').on('click', function () {
82624 context.map().zoomEase(context.minEditableZoom());
82625 }).on('wheel', function (d3_event) {
82626 // let wheel events pass through #4482
82627 var e2 = new WheelEvent(d3_event.type, d3_event);
82628 context.surface().node().dispatchEvent(e2);
82630 button.call(svgIcon('#iD-icon-plus', 'pre-text')).append('span').attr('class', 'label').html(_t.html('zoom_in_edit'));
82632 function disableTooHigh() {
82633 var canEdit = context.map().zoom() >= context.minEditableZoom();
82634 div.style('display', canEdit ? 'none' : 'block');
82637 context.map().on('move.notice', debounce(disableTooHigh, 500));
82642 function uiPhotoviewer(context) {
82643 var dispatch$1 = dispatch('resize');
82645 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
82647 function photoviewer(selection) {
82648 selection.append('button').attr('class', 'thumb-hide').on('click', function () {
82649 if (services.streetside) {
82650 services.streetside.hideViewer(context);
82653 if (services.mapillary) {
82654 services.mapillary.hideViewer(context);
82657 if (services.openstreetcam) {
82658 services.openstreetcam.hideViewer(context);
82660 }).append('div').call(svgIcon('#iD-icon-close'));
82662 function preventDefault(d3_event) {
82663 d3_event.preventDefault();
82666 selection.append('button').attr('class', 'resize-handle-xy').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82670 selection.append('button').attr('class', 'resize-handle-x').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82673 selection.append('button').attr('class', 'resize-handle-y').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82677 function buildResizeListener(target, eventName, dispatch, options) {
82678 var resizeOnX = !!options.resizeOnX;
82679 var resizeOnY = !!options.resizeOnY;
82680 var minHeight = options.minHeight || 240;
82681 var minWidth = options.minWidth || 320;
82688 function startResize(d3_event) {
82689 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
82690 d3_event.preventDefault();
82691 d3_event.stopPropagation();
82692 var mapSize = context.map().dimensions();
82695 var maxWidth = mapSize[0];
82696 var newWidth = clamp(startWidth + d3_event.clientX - startX, minWidth, maxWidth);
82697 target.style('width', newWidth + 'px');
82701 var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
82703 var newHeight = clamp(startHeight + startY - d3_event.clientY, minHeight, maxHeight);
82704 target.style('height', newHeight + 'px');
82707 dispatch.call(eventName, target, utilGetDimensions(target, true));
82710 function clamp(num, min, max) {
82711 return Math.max(min, Math.min(num, max));
82714 function stopResize(d3_event) {
82715 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
82716 d3_event.preventDefault();
82717 d3_event.stopPropagation(); // remove all the listeners we added
82719 select(window).on('.' + eventName, null);
82722 return function initResize(d3_event) {
82723 d3_event.preventDefault();
82724 d3_event.stopPropagation();
82725 pointerId = d3_event.pointerId || 'mouse';
82726 startX = d3_event.clientX;
82727 startY = d3_event.clientY;
82728 var targetRect = target.node().getBoundingClientRect();
82729 startWidth = targetRect.width;
82730 startHeight = targetRect.height;
82731 select(window).on(_pointerPrefix + 'move.' + eventName, startResize, false).on(_pointerPrefix + 'up.' + eventName, stopResize, false);
82733 if (_pointerPrefix === 'pointer') {
82734 select(window).on('pointercancel.' + eventName, stopResize, false);
82740 photoviewer.onMapResize = function () {
82741 var photoviewer = context.container().select('.photoviewer');
82742 var content = context.container().select('.main-content');
82743 var mapDimensions = utilGetDimensions(content, true); // shrink photo viewer if it is too big
82744 // (-90 preserves space at top and bottom of map used by menus)
82746 var photoDimensions = utilGetDimensions(photoviewer, true);
82748 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > mapDimensions[1] - 90) {
82749 var setPhotoDimensions = [Math.min(photoDimensions[0], mapDimensions[0]), Math.min(photoDimensions[1], mapDimensions[1] - 90)];
82750 photoviewer.style('width', setPhotoDimensions[0] + 'px').style('height', setPhotoDimensions[1] + 'px');
82751 dispatch$1.call('resize', photoviewer, setPhotoDimensions);
82755 return utilRebind(photoviewer, dispatch$1, 'on');
82758 function uiRestore(context) {
82759 return function (selection) {
82760 if (!context.history().hasRestorableChanges()) return;
82761 var modalSelection = uiModal(selection, true);
82762 modalSelection.select('.modal').attr('class', 'modal fillL');
82763 var introModal = modalSelection.select('.content');
82764 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('restore.heading'));
82765 introModal.append('div').attr('class', 'modal-section').append('p').html(_t.html('restore.description'));
82766 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
82767 var restore = buttonWrap.append('button').attr('class', 'restore').on('click', function () {
82768 context.history().restore();
82769 modalSelection.remove();
82771 restore.append('svg').attr('class', 'logo logo-restore').append('use').attr('xlink:href', '#iD-logo-restore');
82772 restore.append('div').html(_t.html('restore.restore'));
82773 var reset = buttonWrap.append('button').attr('class', 'reset').on('click', function () {
82774 context.history().clearSaved();
82775 modalSelection.remove();
82777 reset.append('svg').attr('class', 'logo logo-reset').append('use').attr('xlink:href', '#iD-logo-reset');
82778 reset.append('div').html(_t.html('restore.reset'));
82779 restore.node().focus();
82783 function uiScale(context) {
82784 var projection = context.projection,
82785 isImperial = !_mainLocalizer.usesMetric(),
82789 function scaleDefs(loc1, loc2) {
82790 var lat = (loc2[1] + loc1[1]) / 2,
82791 conversion = isImperial ? 3.28084 : 1,
82792 dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
82804 buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
82806 buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
82807 } // determine a user-friendly endpoint for the scale
82810 for (i = 0; i < buckets.length; i++) {
82814 scale.dist = Math.floor(dist / val) * val;
82817 scale.dist = +dist.toFixed(2);
82821 dLon = geoMetersToLon(scale.dist / conversion, lat);
82822 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
82823 scale.text = displayLength(scale.dist / conversion, isImperial);
82827 function update(selection) {
82828 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
82829 var dims = context.map().dimensions(),
82830 loc1 = projection.invert([0, dims[1]]),
82831 loc2 = projection.invert([maxLength, dims[1]]),
82832 scale = scaleDefs(loc1, loc2);
82833 selection.select('.scale-path').attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
82834 selection.select('.scale-text').style(_mainLocalizer.textDirection() === 'ltr' ? 'left' : 'right', scale.px + 16 + 'px').html(scale.text);
82837 return function (selection) {
82838 function switchUnits() {
82839 isImperial = !isImperial;
82840 selection.call(update);
82843 var scalegroup = selection.append('svg').attr('class', 'scale').on('click', switchUnits).append('g').attr('transform', 'translate(10,11)');
82844 scalegroup.append('path').attr('class', 'scale-path');
82845 selection.append('div').attr('class', 'scale-text');
82846 selection.call(update);
82847 context.map().on('move.scale', function () {
82853 function uiShortcuts(context) {
82854 var detected = utilDetect();
82855 var _activeTab = 0;
82857 var _modalSelection;
82859 var _selection = select(null);
82861 function shortcutsModal(_modalSelection) {
82862 _modalSelection.select('.modal').classed('modal-shortcuts', true);
82864 var content = _modalSelection.select('.content');
82866 content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title'));
82867 _mainFileFetcher.get('shortcuts').then(function (data) {
82868 content.call(render, data);
82869 })["catch"](function () {
82874 function render(selection, dataShortcuts) {
82875 var wrapper = selection.selectAll('.wrapper').data([0]);
82876 var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section');
82877 var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar');
82878 var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list');
82879 wrapper = wrapper.merge(wrapperEnter);
82880 var tabs = tabsBar.selectAll('.tab').data(dataShortcuts);
82881 var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event) {
82882 d3_event.preventDefault();
82883 var i = tabs.nodes().indexOf(this);
82885 render(selection, dataShortcuts);
82887 tabsEnter.append('span').html(function (d) {
82888 return _t.html(d.text);
82891 wrapper.selectAll('.tab').classed('active', function (d, i) {
82892 return i === _activeTab;
82894 var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(dataShortcuts);
82895 var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) {
82896 return 'shortcut-tab shortcut-tab-' + d.tab;
82898 var columnsEnter = shortcutsEnter.selectAll('.shortcut-column').data(function (d) {
82900 }).enter().append('table').attr('class', 'shortcut-column');
82901 var rowsEnter = columnsEnter.selectAll('.shortcut-row').data(function (d) {
82903 }).enter().append('tr').attr('class', 'shortcut-row');
82904 var sectionRows = rowsEnter.filter(function (d) {
82905 return !d.shortcuts;
82907 sectionRows.append('td');
82908 sectionRows.append('td').attr('class', 'shortcut-section').append('h3').html(function (d) {
82909 return _t.html(d.text);
82911 var shortcutRows = rowsEnter.filter(function (d) {
82912 return d.shortcuts;
82914 var shortcutKeys = shortcutRows.append('td').attr('class', 'shortcut-keys');
82915 var modifierKeys = shortcutKeys.filter(function (d) {
82916 return d.modifiers;
82918 modifierKeys.selectAll('kbd.modifier').data(function (d) {
82919 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
82921 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
82924 return d.modifiers;
82926 }).enter().each(function () {
82927 var selection = select(this);
82928 selection.append('kbd').attr('class', 'modifier').html(function (d) {
82929 return uiCmd.display(d);
82931 selection.append('span').html('+');
82933 shortcutKeys.selectAll('kbd.shortcut').data(function (d) {
82934 var arr = d.shortcuts;
82936 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
82938 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
82940 } // replace translations
82943 arr = arr.map(function (s) {
82944 return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
82946 return utilArrayUniq(arr).map(function (s) {
82949 separator: d.separator,
82953 }).enter().each(function (d, i, nodes) {
82954 var selection = select(this);
82955 var click = d.shortcut.toLowerCase().match(/(.*).click/);
82957 if (click && click[1]) {
82958 // replace "left_click", "right_click" with mouse icon
82959 selection.call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
82960 } else if (d.shortcut.toLowerCase() === 'long-press') {
82961 selection.call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
82962 } else if (d.shortcut.toLowerCase() === 'tap') {
82963 selection.call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
82965 selection.append('kbd').attr('class', 'shortcut').html(function (d) {
82970 if (i < nodes.length - 1) {
82971 selection.append('span').html(d.separator || "\xA0" + _t.html('shortcuts.or') + "\xA0");
82972 } else if (i === nodes.length - 1 && d.suffix) {
82973 selection.append('span').html(d.suffix);
82976 shortcutKeys.filter(function (d) {
82978 }).each(function () {
82979 var selection = select(this);
82980 selection.append('span').html('+');
82981 selection.append('span').attr('class', 'gesture').html(function (d) {
82982 return _t.html(d.gesture);
82985 shortcutRows.append('td').attr('class', 'shortcut-desc').html(function (d) {
82986 return d.text ? _t.html(d.text) : "\xA0";
82989 wrapper.selectAll('.shortcut-tab').style('display', function (d, i) {
82990 return i === _activeTab ? 'flex' : 'none';
82994 return function (selection, show) {
82995 _selection = selection;
82998 _modalSelection = uiModal(selection);
83000 _modalSelection.call(shortcutsModal);
83002 context.keybinding().on([_t('shortcuts.toggle.key'), '?'], function () {
83003 if (context.container().selectAll('.modal-shortcuts').size()) {
83005 if (_modalSelection) {
83006 _modalSelection.close();
83008 _modalSelection = null;
83011 _modalSelection = uiModal(_selection);
83013 _modalSelection.call(shortcutsModal);
83022 function search(input, dims) {
83023 if (!dims) dims = 'NSEW';
83024 if (typeof input !== 'string') return null;
83025 input = input.toUpperCase();
83026 var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
83027 var m = input.match(regex);
83028 if (!m) return null; // no match
83030 var matched = m[0]; // extract dimension.. m[1] = leading, m[5] = trailing
83034 if (m[1] && m[5]) {
83035 // if matched both..
83036 dim = m[1]; // keep leading
83038 matched = matched.slice(0, -1); // remove trailing dimension from match
83040 dim = m[1] || m[5];
83041 } // if unrecognized dimension
83044 if (dim && dims.indexOf(dim) === -1) return null; // extract DMS
83046 var deg = m[2] ? parseFloat(m[2]) : 0;
83047 var min = m[3] ? parseFloat(m[3]) / 60 : 0;
83048 var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
83049 var sign = deg < 0 ? -1 : 1;
83050 if (dim === 'S' || dim === 'W') sign *= -1;
83052 val: (Math.abs(deg) + min + sec) * sign,
83055 remain: input.slice(matched.length)
83059 function pair(input, dims) {
83060 input = input.trim();
83061 var one = search(input, dims);
83062 if (!one) return null;
83063 input = one.remain.trim();
83064 var two = search(input, dims);
83065 if (!two || two.remain) return null;
83068 return swapdim(one.val, two.val, one.dim);
83070 return [one.val, two.val];
83074 function swapdim(a, b, dim) {
83075 if (dim === 'N' || dim === 'S') return [a, b];
83076 if (dim === 'W' || dim === 'E') return [b, a];
83079 function uiFeatureList(context) {
83080 var _geocodeResults;
83082 function featureList(selection) {
83083 var header = selection.append('div').attr('class', 'header fillL');
83084 header.append('h3').html(_t.html('inspector.feature_list'));
83085 var searchWrap = selection.append('div').attr('class', 'search-header');
83086 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
83087 var search = searchWrap.append('input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keypress', keypress).on('keydown', keydown).on('input', inputevent);
83088 var listWrap = selection.append('div').attr('class', 'inspector-body');
83089 var list = listWrap.append('div').attr('class', 'feature-list');
83090 context.on('exit.feature-list', clearSearch);
83091 context.map().on('drawn.feature-list', mapDrawn);
83092 context.keybinding().on(uiCmd('⌘F'), focusSearch);
83094 function focusSearch(d3_event) {
83095 var mode = context.mode() && context.mode().id;
83096 if (mode !== 'browse') return;
83097 d3_event.preventDefault();
83098 search.node().focus();
83101 function keydown(d3_event) {
83102 if (d3_event.keyCode === 27) {
83104 search.node().blur();
83108 function keypress(d3_event) {
83109 var q = search.property('value'),
83110 items = list.selectAll('.feature-list-item');
83112 if (d3_event.keyCode === 13 && // ↩ Return
83113 q.length && items.size()) {
83114 click(items.datum());
83118 function inputevent() {
83119 _geocodeResults = undefined;
83123 function clearSearch() {
83124 search.property('value', '');
83128 function mapDrawn(e) {
83134 function features() {
83136 var graph = context.graph();
83137 var visibleCenter = context.map().extent().center();
83138 var q = search.property('value').toLowerCase();
83139 if (!q) return result;
83140 var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
83142 if (locationMatch) {
83143 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
83147 type: _t('inspector.location'),
83148 name: dmsCoordinatePair([loc[1], loc[0]]),
83151 } // A location search takes priority over an ID search
83154 var idMatch = !locationMatch && q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
83157 var elemType = idMatch[1].charAt(0);
83158 var elemId = idMatch[2];
83160 id: elemType + elemId,
83161 geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
83162 type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
83167 var allEntities = graph.entities;
83168 var localResults = [];
83170 for (var id in allEntities) {
83171 var entity = allEntities[id];
83172 if (!entity) continue;
83173 var name = utilDisplayName(entity) || '';
83174 if (name.toLowerCase().indexOf(q) < 0) continue;
83175 var matched = _mainPresetIndex.match(entity, graph);
83176 var type = matched && matched.name() || utilDisplayType(entity.id);
83177 var extent = entity.extent(graph);
83178 var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
83179 localResults.push({
83182 geometry: entity.geometry(graph),
83187 if (localResults.length > 100) break;
83190 localResults = localResults.sort(function byDistance(a, b) {
83191 return a.distance - b.distance;
83193 result = result.concat(localResults);
83195 (_geocodeResults || []).forEach(function (d) {
83196 if (d.osm_type && d.osm_id) {
83197 // some results may be missing these - #1890
83198 // Make a temporary osmEntity so we can preset match
83199 // and better localize the search result - #4725
83200 var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
83202 tags[d["class"]] = d.type;
83209 if (d.osm_type === 'way') {
83210 // for ways, add some fake closed nodes
83211 attrs.nodes = ['a', 'a']; // so that geometry area is possible
83214 var tempEntity = osmEntity(attrs);
83215 var tempGraph = coreGraph([tempEntity]);
83216 var matched = _mainPresetIndex.match(tempEntity, tempGraph);
83217 var type = matched && matched.name() || utilDisplayType(id);
83220 geometry: tempEntity.geometry(tempGraph),
83222 name: d.display_name,
83223 extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
83228 if (q.match(/^[0-9]+$/)) {
83229 // if query is just a number, possibly an OSM ID without a prefix
83233 type: _t('inspector.node'),
83239 type: _t('inspector.way'),
83244 geometry: 'relation',
83245 type: _t('inspector.relation'),
83253 function drawList() {
83254 var value = search.property('value');
83255 var results = features();
83256 list.classed('filtered', value.length);
83257 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'));
83258 resultsIndicator.append('span').attr('class', 'entity-name');
83259 list.selectAll('.no-results-item .entity-name').html(_t.html('geocoder.no_results_worldwide'));
83261 if (services.geocoder) {
83262 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'));
83265 list.selectAll('.no-results-item').style('display', value.length && !results.length ? 'block' : 'none');
83266 list.selectAll('.geocode-item').style('display', value && _geocodeResults === undefined ? 'block' : 'none');
83267 list.selectAll('.feature-list-item').data([-1]).remove();
83268 var items = list.selectAll('.feature-list-item').data(results, function (d) {
83271 var enter = items.enter().insert('button', '.geocode-item').attr('class', 'feature-list-item').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
83272 var label = enter.append('div').attr('class', 'label');
83273 label.each(function (d) {
83274 select(this).call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
83276 label.append('span').attr('class', 'entity-type').html(function (d) {
83279 label.append('span').attr('class', 'entity-name').html(function (d) {
83282 enter.style('opacity', 0).transition().style('opacity', 1);
83284 items.exit().remove();
83287 function mouseover(d3_event, d) {
83288 if (d.id === -1) return;
83289 utilHighlightEntities([d.id], true, context);
83292 function mouseout(d3_event, d) {
83293 if (d.id === -1) return;
83294 utilHighlightEntities([d.id], false, context);
83297 function click(d3_event, d) {
83298 d3_event.preventDefault();
83301 context.map().centerZoomEase([d.location[1], d.location[0]], 19);
83302 } else if (d.entity) {
83303 utilHighlightEntities([d.id], false, context);
83304 context.enter(modeSelect(context, [d.entity.id]));
83305 context.map().zoomToEase(d.entity);
83307 // download, zoom to, and select the entity with the given ID
83308 context.zoomToEntity(d.id);
83312 function geocoderSearch() {
83313 services.geocoder.search(search.property('value'), function (err, resp) {
83314 _geocodeResults = resp || [];
83320 return featureList;
83323 function uiSectionEntityIssues(context) {
83324 var _entityIDs = [];
83327 var _activeIssueID;
83329 var section = uiSection('entity-issues', context).shouldDisplay(function () {
83330 return _issues.length > 0;
83331 }).label(function () {
83332 return _t('inspector.title_count', {
83333 title: _t.html('issues.list_title'),
83334 count: _issues.length
83336 }).disclosureContent(renderDisclosureContent);
83337 context.validator().on('validated.entity_issues', function () {
83338 // Refresh on validated events
83340 section.reRender();
83341 }).on('focusedIssue.entity_issues', function (issue) {
83342 makeActiveIssue(issue.id);
83345 function reloadIssues() {
83346 _issues = context.validator().getSharedEntityIssues(_entityIDs, {
83347 includeDisabledRules: true
83351 function makeActiveIssue(issueID) {
83352 _activeIssueID = issueID;
83353 section.selection().selectAll('.issue-container').classed('active', function (d) {
83354 return d.id === _activeIssueID;
83358 function renderDisclosureContent(selection) {
83359 selection.classed('grouped-items-area', true);
83360 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
83361 var containers = selection.selectAll('.issue-container').data(_issues, function (d) {
83365 containers.exit().remove(); // Enter
83367 var containersEnter = containers.enter().append('div').attr('class', 'issue-container');
83368 var itemsEnter = containersEnter.append('div').attr('class', function (d) {
83369 return 'issue severity-' + d.severity;
83370 }).on('mouseover.highlight', function (d3_event, d) {
83371 // don't hover-highlight the selected entity
83372 var ids = d.entityIds.filter(function (e) {
83373 return _entityIDs.indexOf(e) === -1;
83375 utilHighlightEntities(ids, true, context);
83376 }).on('mouseout.highlight', function (d3_event, d) {
83377 var ids = d.entityIds.filter(function (e) {
83378 return _entityIDs.indexOf(e) === -1;
83380 utilHighlightEntities(ids, false, context);
83382 var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
83383 var textEnter = labelsEnter.append('button').attr('class', 'issue-text').on('click', function (d3_event, d) {
83384 makeActiveIssue(d.id); // expand only the clicked item
83386 var extent = d.extent(context.graph());
83389 var setZoom = Math.max(context.map().zoom(), 19);
83390 context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
83393 textEnter.each(function (d) {
83394 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
83395 select(this).call(svgIcon(iconName, 'issue-icon'));
83397 textEnter.append('span').attr('class', 'issue-message');
83398 var infoButton = labelsEnter.append('button').attr('class', 'issue-info-button').attr('title', _t('icons.information')).call(svgIcon('#iD-icon-inspect'));
83399 infoButton.on('click', function (d3_event) {
83400 d3_event.stopPropagation();
83401 d3_event.preventDefault();
83402 this.blur(); // avoid keeping focus on the button - #4641
83404 var container = select(this.parentNode.parentNode.parentNode);
83405 var info = container.selectAll('.issue-info');
83406 var isExpanded = info.classed('expanded');
83409 info.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
83410 info.classed('expanded', false);
83413 info.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1').on('end', function () {
83414 info.style('max-height', null);
83418 itemsEnter.append('ul').attr('class', 'issue-fix-list');
83419 containersEnter.append('div').attr('class', 'issue-info').style('max-height', '0').style('opacity', '0').each(function (d) {
83420 if (typeof d.reference === 'function') {
83421 select(this).call(d.reference);
83423 select(this).html(_t.html('inspector.no_documentation_key'));
83427 containers = containers.merge(containersEnter).classed('active', function (d) {
83428 return d.id === _activeIssueID;
83430 containers.selectAll('.issue-message').html(function (d) {
83431 return d.message(context);
83434 var fixLists = containers.selectAll('.issue-fix-list');
83435 var fixes = fixLists.selectAll('.issue-fix-item').data(function (d) {
83436 return d.fixes ? d.fixes(context) : [];
83437 }, function (fix) {
83440 fixes.exit().remove();
83441 var fixesEnter = fixes.enter().append('li').attr('class', 'issue-fix-item');
83442 var buttons = fixesEnter.append('button').on('click', function (d3_event, d) {
83443 // not all fixes are actionable
83444 if (select(this).attr('disabled') || !d.onClick) return; // Don't run another fix for this issue within a second of running one
83445 // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
83447 if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
83448 d.issue.dateLastRanFix = new Date(); // remove hover-highlighting
83450 utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
83451 new Promise(function (resolve, reject) {
83452 d.onClick(context, resolve, reject);
83454 if (d.onClick.length <= 1) {
83455 // if the fix doesn't take any completion parameters then consider it resolved
83458 }).then(function () {
83459 // revalidate whenever the fix has finished running successfully
83460 context.validator().validate();
83462 }).on('mouseover.highlight', function (d3_event, d) {
83463 utilHighlightEntities(d.entityIds, true, context);
83464 }).on('mouseout.highlight', function (d3_event, d) {
83465 utilHighlightEntities(d.entityIds, false, context);
83467 buttons.each(function (d) {
83468 var iconName = d.icon || 'iD-icon-wrench';
83470 if (iconName.startsWith('maki')) {
83474 select(this).call(svgIcon('#' + iconName, 'fix-icon'));
83476 buttons.append('span').attr('class', 'fix-message').html(function (d) {
83479 fixesEnter.merge(fixes).selectAll('button').classed('actionable', function (d) {
83481 }).attr('disabled', function (d) {
83482 return d.onClick ? null : 'true';
83483 }).attr('title', function (d) {
83484 if (d.disabledReason) {
83485 return d.disabledReason;
83492 section.entityIDs = function (val) {
83493 if (!arguments.length) return _entityIDs;
83495 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
83497 _activeIssueID = null;
83507 function uiPresetIcon() {
83512 var _sizeClass = 'medium';
83514 function isSmall() {
83515 return _sizeClass === 'small';
83518 function presetIcon(selection) {
83519 selection.each(render);
83522 function getIcon(p, geom) {
83523 if (isSmall() && p.isFallback && p.isFallback()) return 'iD-icon-' + p.id;else if (p.icon) return p.icon;else if (geom === 'line') return 'iD-other-line';else if (geom === 'vertex') return p.isFallback() ? '' : 'temaki-vertex';else if (isSmall() && geom === 'point') return '';else return 'maki-marker-stroked';
83526 function renderPointBorder(container, drawPoint) {
83527 var pointBorder = container.selectAll('.preset-icon-point-border').data(drawPoint ? [0] : []);
83528 pointBorder.exit().remove();
83529 var pointBorderEnter = pointBorder.enter();
83532 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');
83533 pointBorder = pointBorderEnter.merge(pointBorder);
83536 function renderCircleFill(container, drawVertex) {
83537 var vertexFill = container.selectAll('.preset-icon-fill-vertex').data(drawVertex ? [0] : []);
83538 vertexFill.exit().remove();
83539 var vertexFillEnter = vertexFill.enter();
83543 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);
83544 vertexFill = vertexFillEnter.merge(vertexFill);
83547 function renderSquareFill(container, drawArea, tagClasses) {
83548 var fill = container.selectAll('.preset-icon-fill-area').data(drawArea ? [0] : []);
83549 fill.exit().remove();
83550 var fillEnter = fill.enter();
83551 var d = isSmall() ? 40 : 60;
83555 var c1 = (w - l) / 2;
83557 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));
83558 ['fill', 'stroke'].forEach(function (klass) {
83559 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', "line area ".concat(klass));
83562 [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
83563 fillEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', rVertex);
83567 var rMidpoint = 1.25;
83568 [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach(function (point) {
83569 fillEnter.append('circle').attr('class', 'midpoint').attr('cx', point[0]).attr('cy', point[1]).attr('r', rMidpoint);
83573 fill = fillEnter.merge(fill);
83574 fill.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
83575 fill.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
83578 function renderLine(container, drawLine, tagClasses) {
83579 var line = container.selectAll('.preset-icon-line').data(drawLine ? [0] : []);
83580 line.exit().remove();
83581 var lineEnter = line.enter();
83582 var d = isSmall() ? 40 : 60; // draw the line parametrically
83586 var y = Math.round(d * 0.72);
83587 var l = Math.round(d * 0.6);
83589 var x1 = (w - l) / 2;
83591 lineEnter = lineEnter.append('svg').attr('class', 'preset-icon-line').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
83592 ['casing', 'stroke'].forEach(function (klass) {
83593 lineEnter.append('path').attr('d', "M".concat(x1, " ").concat(y, " L").concat(x2, " ").concat(y)).attr('class', "line ".concat(klass));
83595 [[x1 - 1, y], [x2 + 1, y]].forEach(function (point) {
83596 lineEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
83598 line = lineEnter.merge(line);
83599 line.selectAll('path.stroke').attr('class', "line stroke ".concat(tagClasses));
83600 line.selectAll('path.casing').attr('class', "line casing ".concat(tagClasses));
83603 function renderRoute(container, drawRoute, p) {
83604 var route = container.selectAll('.preset-icon-route').data(drawRoute ? [0] : []);
83605 route.exit().remove();
83606 var routeEnter = route.enter();
83607 var d = isSmall() ? 40 : 60; // draw the route parametrically
83611 var y1 = Math.round(d * 0.80);
83612 var y2 = Math.round(d * 0.68);
83613 var l = Math.round(d * 0.6);
83615 var x1 = (w - l) / 2;
83616 var x2 = x1 + l / 3;
83617 var x3 = x2 + l / 3;
83618 var x4 = x3 + l / 3;
83619 routeEnter = routeEnter.append('svg').attr('class', 'preset-icon-route').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
83620 ['casing', 'stroke'].forEach(function (klass) {
83621 routeEnter.append('path').attr('d', "M".concat(x1, " ").concat(y1, " L").concat(x2, " ").concat(y2)).attr('class', "segment0 line ".concat(klass));
83622 routeEnter.append('path').attr('d', "M".concat(x2, " ").concat(y2, " L").concat(x3, " ").concat(y1)).attr('class', "segment1 line ".concat(klass));
83623 routeEnter.append('path').attr('d', "M".concat(x3, " ").concat(y1, " L").concat(x4, " ").concat(y2)).attr('class', "segment2 line ".concat(klass));
83625 [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
83626 routeEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
83628 route = routeEnter.merge(route);
83631 var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
83632 var segmentPresetIDs = routeSegments[routeType];
83634 for (var i in segmentPresetIDs) {
83635 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
83636 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
83637 route.selectAll("path.stroke.segment".concat(i)).attr('class', "segment".concat(i, " line stroke ").concat(segmentTagClasses));
83638 route.selectAll("path.casing.segment".concat(i)).attr('class', "segment".concat(i, " line casing ").concat(segmentTagClasses));
83641 } // Route icons are drawn with a zigzag annotation underneath:
83645 // This dataset defines the styles that are used to draw the zigzag segments.
83648 var routeSegments = {
83649 bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
83650 bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
83651 trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
83652 detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
83653 ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
83654 foot: ['highway/footway', 'highway/footway', 'highway/footway'],
83655 hiking: ['highway/path', 'highway/path', 'highway/path'],
83656 horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
83657 light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
83658 monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
83659 pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
83660 piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
83661 power: ['power/line', 'power/line', 'power/line'],
83662 road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
83663 subway: ['railway/subway', 'railway/subway', 'railway/subway'],
83664 train: ['railway/rail', 'railway/rail', 'railway/rail'],
83665 tram: ['railway/tram', 'railway/tram', 'railway/tram'],
83666 waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
83669 function render() {
83670 var p = _preset.apply(this, arguments);
83672 var geom = _geometry ? _geometry.apply(this, arguments) : null;
83674 if (geom === 'relation' && p.tags && (p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route] || p.tags.type === 'waterway')) {
83678 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
83679 var isFallback = isSmall() && p.isFallback && p.isFallback();
83680 var imageURL = showThirdPartyIcons === 'true' && p.imageURL;
83681 var picon = getIcon(p, geom);
83682 var isMaki = picon && /^maki-/.test(picon);
83683 var isTemaki = picon && /^temaki-/.test(picon);
83684 var isFa = picon && /^fa[srb]-/.test(picon);
83685 var isiDIcon = picon && !(isMaki || isTemaki || isFa);
83686 var isCategory = !p.setTags;
83687 var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
83688 var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
83689 var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
83690 var drawArea = picon && geom === 'area' && !isFallback;
83691 var drawRoute = picon && geom === 'route';
83692 var isFramed = drawVertex || drawArea || drawLine || drawRoute;
83693 var tags = !isCategory ? p.setTags({}, geom) : {};
83695 for (var k in tags) {
83696 if (tags[k] === '*') {
83701 var tagClasses = svgTagClasses().getClassesString(tags, '');
83702 var selection = select(this);
83703 var container = selection.selectAll('.preset-icon-container').data([0]);
83704 container = container.enter().append('div').attr('class', "preset-icon-container ".concat(_sizeClass)).merge(container);
83705 container.classed('showing-img', !!imageURL).classed('fallback', isFallback);
83706 renderPointBorder(container, drawPoint);
83707 renderCircleFill(container, drawVertex);
83708 renderSquareFill(container, drawArea, tagClasses);
83709 renderLine(container, drawLine, tagClasses);
83710 renderRoute(container, drawRoute, p);
83711 var icon = container.selectAll('.preset-icon').data(picon ? [0] : []);
83712 icon.exit().remove();
83713 icon = icon.enter().append('div').attr('class', 'preset-icon').call(svgIcon('')).merge(icon);
83714 icon.attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')).classed('framed', isFramed).classed('preset-icon-iD', isiDIcon);
83715 icon.selectAll('svg').attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses));
83716 icon.selectAll('use').attr('href', '#' + picon + (isMaki ? isSmall() && geom === 'point' ? '-11' : '-15' : ''));
83717 var imageIcon = container.selectAll('img.image-icon').data(imageURL ? [0] : []);
83718 imageIcon.exit().remove();
83719 imageIcon = imageIcon.enter().append('img').attr('class', 'image-icon').on('load', function () {
83720 return container.classed('showing-img', true);
83721 }).on('error', function () {
83722 return container.classed('showing-img', false);
83723 }).merge(imageIcon);
83724 imageIcon.attr('src', imageURL);
83727 presetIcon.preset = function (val) {
83728 if (!arguments.length) return _preset;
83729 _preset = utilFunctor(val);
83733 presetIcon.geometry = function (val) {
83734 if (!arguments.length) return _geometry;
83735 _geometry = utilFunctor(val);
83739 presetIcon.sizeClass = function (val) {
83740 if (!arguments.length) return _sizeClass;
83748 function uiSectionFeatureType(context) {
83749 var dispatch$1 = dispatch('choose');
83750 var _entityIDs = [];
83755 var section = uiSection('feature-type', context).label(_t.html('inspector.feature_type')).disclosureContent(renderDisclosureContent);
83757 function renderDisclosureContent(selection) {
83758 selection.classed('preset-list-item', true);
83759 selection.classed('mixed-types', _presets.length > 1);
83760 var presetButtonWrap = selection.selectAll('.preset-list-button-wrap').data([0]).enter().append('div').attr('class', 'preset-list-button-wrap');
83761 var presetButton = presetButtonWrap.append('button').attr('class', 'preset-list-button preset-reset').call(uiTooltip().title(_t.html('inspector.back_tooltip')).placement('bottom'));
83762 presetButton.append('div').attr('class', 'preset-icon-container');
83763 presetButton.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
83764 presetButtonWrap.append('div').attr('class', 'accessory-buttons');
83765 var tagReferenceBodyWrap = selection.selectAll('.tag-reference-body-wrap').data([0]);
83766 tagReferenceBodyWrap = tagReferenceBodyWrap.enter().append('div').attr('class', 'tag-reference-body-wrap').merge(tagReferenceBodyWrap); // update header
83768 if (_tagReference) {
83769 selection.selectAll('.preset-list-button-wrap .accessory-buttons').style('display', _presets.length === 1 ? null : 'none').call(_tagReference.button);
83770 tagReferenceBodyWrap.style('display', _presets.length === 1 ? null : 'none').call(_tagReference.body);
83773 selection.selectAll('.preset-reset').on('click', function () {
83774 dispatch$1.call('choose', this, _presets);
83775 }).on('pointerdown pointerup mousedown mouseup', function (d3_event) {
83776 d3_event.preventDefault();
83777 d3_event.stopPropagation();
83779 var geometries = entityGeometries();
83780 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')));
83781 var names = _presets.length === 1 ? [_presets[0].nameLabel(), _presets[0].subtitleLabel()].filter(Boolean) : [_t('inspector.multiple_types')];
83782 var label = selection.select('.label-inner');
83783 var nameparts = label.selectAll('.namepart').data(names, function (d) {
83786 nameparts.exit().remove();
83787 nameparts.enter().append('div').attr('class', 'namepart').html(function (d) {
83792 section.entityIDs = function (val) {
83793 if (!arguments.length) return _entityIDs;
83798 section.presets = function (val) {
83799 if (!arguments.length) return _presets; // don't reload the same preset
83801 if (!utilArrayIdentical(val, _presets)) {
83804 if (_presets.length === 1) {
83805 _tagReference = uiTagReference(_presets[0].reference()).showing(false);
83812 function entityGeometries() {
83815 for (var i in _entityIDs) {
83816 var geometry = context.graph().geometry(_entityIDs[i]);
83817 if (!counts[geometry]) counts[geometry] = 0;
83818 counts[geometry] += 1;
83821 return Object.keys(counts).sort(function (geom1, geom2) {
83822 return counts[geom2] - counts[geom1];
83826 return utilRebind(section, dispatch$1, 'on');
83829 // It borrows some code from uiHelp
83831 function uiFieldHelp(context, fieldName) {
83832 var fieldHelp = {};
83834 var _inspector = select(null);
83836 var _wrap = select(null);
83838 var _body = select(null);
83840 var fieldHelpKeys = {
83841 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']]]
83843 var fieldHelpHeadings = {};
83844 var replacements = {
83845 distField: _t.html('restriction.controls.distance'),
83846 viaField: _t.html('restriction.controls.via'),
83847 fromShadow: icon('#iD-turn-shadow', 'inline shadow from'),
83848 allowShadow: icon('#iD-turn-shadow', 'inline shadow allow'),
83849 restrictShadow: icon('#iD-turn-shadow', 'inline shadow restrict'),
83850 onlyShadow: icon('#iD-turn-shadow', 'inline shadow only'),
83851 allowTurn: icon('#iD-turn-yes', 'inline turn'),
83852 restrictTurn: icon('#iD-turn-no', 'inline turn'),
83853 onlyTurn: icon('#iD-turn-only', 'inline turn')
83854 }; // For each section, squash all the texts into a single markdown document
83856 var docs = fieldHelpKeys[fieldName].map(function (key) {
83857 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83858 var text = key[1].reduce(function (all, part) {
83859 var subkey = helpkey + '.' + part;
83860 var depth = fieldHelpHeadings[subkey]; // is this subkey a heading?
83862 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
83864 return all + hhh + _t.html(subkey, replacements) + '\n\n';
83868 title: _t.html(helpkey + '.title'),
83869 html: marked_1(text.trim())
83876 _body.classed('hide', false).style('opacity', '0').transition().duration(200).style('opacity', '1');
83880 _body.classed('hide', true).transition().duration(200).style('opacity', '0').on('end', function () {
83881 _body.classed('hide', true);
83885 function clickHelp(index) {
83886 var d = docs[index];
83887 var tkeys = fieldHelpKeys[fieldName][index][1];
83889 _body.selectAll('.field-help-nav-item').classed('active', function (d, i) {
83890 return i === index;
83893 var content = _body.selectAll('.field-help-content').html(d.html); // class the paragraphs so we can find and style them
83896 content.selectAll('p').attr('class', function (d, i) {
83898 }); // insert special content for certain help sections
83900 if (d.key === 'help.field.restrictions.inspecting') {
83901 content.insert('img', 'p.from_shadow').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_inspect.gif'));
83902 } else if (d.key === 'help.field.restrictions.modifying') {
83903 content.insert('img', 'p.allow_turn').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_modify.gif'));
83907 fieldHelp.button = function (selection) {
83908 if (_body.empty()) return;
83909 var button = selection.selectAll('.field-help-button').data([0]); // enter/update
83911 button.enter().append('button').attr('class', 'field-help-button').call(svgIcon('#iD-icon-help')).merge(button).on('click', function (d3_event) {
83912 d3_event.stopPropagation();
83913 d3_event.preventDefault();
83915 if (_body.classed('hide')) {
83923 function updatePosition() {
83924 var wrap = _wrap.node();
83926 var inspector = _inspector.node();
83928 var wRect = wrap.getBoundingClientRect();
83929 var iRect = inspector.getBoundingClientRect();
83931 _body.style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
83934 fieldHelp.body = function (selection) {
83935 // This control expects the field to have a form-field-input-wrap div
83936 _wrap = selection.selectAll('.form-field-input-wrap');
83937 if (_wrap.empty()) return; // absolute position relative to the inspector, so it "floats" above the fields
83939 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
83940 if (_inspector.empty()) return;
83941 _body = _inspector.selectAll('.field-help-body').data([0]);
83943 var enter = _body.enter().append('div').attr('class', 'field-help-body hide'); // initially hidden
83946 var titleEnter = enter.append('div').attr('class', 'field-help-title cf');
83947 titleEnter.append('h2').attr('class', _mainLocalizer.textDirection() === 'rtl' ? 'fr' : 'fl').html(_t.html('help.field.' + fieldName + '.title'));
83948 titleEnter.append('button').attr('class', 'fr close').on('click', function (d3_event) {
83949 d3_event.stopPropagation();
83950 d3_event.preventDefault();
83952 }).call(svgIcon('#iD-icon-close'));
83953 var navEnter = enter.append('div').attr('class', 'field-help-nav cf');
83954 var titles = docs.map(function (d) {
83957 navEnter.selectAll('.field-help-nav-item').data(titles).enter().append('div').attr('class', 'field-help-nav-item').html(function (d) {
83959 }).on('click', function (d3_event, d) {
83960 d3_event.stopPropagation();
83961 d3_event.preventDefault();
83962 clickHelp(titles.indexOf(d));
83964 enter.append('div').attr('class', 'field-help-content');
83965 _body = _body.merge(enter);
83972 function uiFieldCheck(field, context) {
83973 var dispatch$1 = dispatch('change');
83974 var options = field.strings && field.strings.options;
83980 var input = select(null);
83981 var text = select(null);
83982 var label = select(null);
83983 var reverser = select(null);
83987 var _entityIDs = [];
83992 for (var k in options) {
83993 values.push(k === 'undefined' ? undefined : k);
83994 texts.push(field.t.html('options.' + k, {
83995 'default': options[k]
83999 values = [undefined, 'yes'];
84000 texts = [_t.html('inspector.unknown'), _t.html('inspector.check.yes')];
84002 if (field.type !== 'defaultCheck') {
84004 texts.push(_t.html('inspector.check.no'));
84006 } // Checks tags to see whether an undefined value is "Assumed to be Yes"
84009 function checkImpliedYes() {
84010 _impliedYes = field.id === 'oneway_yes'; // hack: pretend `oneway` field is a `oneway_yes` field
84011 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
84013 if (field.id === 'oneway') {
84014 var entity = context.entity(_entityIDs[0]);
84016 for (var key in entity.tags) {
84017 if (key in osmOneWayTags && entity.tags[key] in osmOneWayTags[key]) {
84018 _impliedYes = true;
84019 texts[0] = _t.html('presets.fields.oneway_yes.options.undefined');
84026 function reverserHidden() {
84027 if (!context.container().select('div.inspector-hover').empty()) return true;
84028 return !(_value === 'yes' || _impliedYes && !_value);
84031 function reverserSetText(selection) {
84032 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84033 if (reverserHidden() || !entity) return selection;
84034 var first = entity.first();
84035 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
84036 var pseudoDirection = first < last;
84037 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
84038 selection.selectAll('.reverser-span').html(_t.html('inspector.check.reverser')).call(svgIcon(icon, 'inline'));
84042 var check = function check(selection) {
84044 label = selection.selectAll('.form-field-input-wrap').data([0]);
84045 var enter = label.enter().append('label').attr('class', 'form-field-input-wrap form-field-input-check');
84046 enter.append('input').property('indeterminate', field.type !== 'defaultCheck').attr('type', 'checkbox').attr('id', field.domId);
84047 enter.append('span').html(texts[0]).attr('class', 'value');
84049 if (field.type === 'onewayCheck') {
84050 enter.append('button').attr('class', 'reverser' + (reverserHidden() ? ' hide' : '')).append('span').attr('class', 'reverser-span');
84053 label = label.merge(enter);
84054 input = label.selectAll('input');
84055 text = label.selectAll('span.value');
84056 input.on('click', function (d3_event) {
84057 d3_event.stopPropagation();
84060 if (Array.isArray(_tags[field.key])) {
84061 if (values.indexOf('yes') !== -1) {
84062 t[field.key] = 'yes';
84064 t[field.key] = values[0];
84067 t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
84068 } // Don't cycle through `alternating` or `reversible` states - #4970
84069 // (They are supported as translated strings, but should not toggle with clicks)
84072 if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
84073 t[field.key] = values[0];
84076 dispatch$1.call('change', this, t);
84079 if (field.type === 'onewayCheck') {
84080 reverser = label.selectAll('.reverser');
84081 reverser.call(reverserSetText).on('click', function (d3_event) {
84082 d3_event.preventDefault();
84083 d3_event.stopPropagation();
84084 context.perform(function (graph) {
84085 for (var i in _entityIDs) {
84086 graph = actionReverse(_entityIDs[i])(graph);
84090 }, _t('operations.reverse.annotation.line', {
84092 })); // must manually revalidate since no 'change' event was called
84094 context.validator().validate();
84095 select(this).call(reverserSetText);
84100 check.entityIDs = function (val) {
84101 if (!arguments.length) return _entityIDs;
84106 check.tags = function (tags) {
84109 function isChecked(val) {
84110 return val !== 'no' && val !== '' && val !== undefined && val !== null;
84113 function textFor(val) {
84114 if (val === '') val = undefined;
84115 var index = values.indexOf(val);
84116 return index !== -1 ? texts[index] : '"' + val + '"';
84120 var isMixed = Array.isArray(tags[field.key]);
84121 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
84123 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
84127 input.property('indeterminate', isMixed || field.type !== 'defaultCheck' && !_value).property('checked', isChecked(_value));
84128 text.html(isMixed ? _t.html('inspector.multiple_values') : textFor(_value)).classed('mixed', isMixed);
84129 label.classed('set', !!_value);
84131 if (field.type === 'onewayCheck') {
84132 reverser.classed('hide', reverserHidden()).call(reverserSetText);
84136 check.focus = function () {
84137 input.node().focus();
84140 return utilRebind(check, dispatch$1, 'on');
84143 function uiFieldCombo(field, context) {
84144 var dispatch$1 = dispatch('change');
84146 var _isMulti = field.type === 'multiCombo' || field.type === 'manyCombo';
84148 var _isNetwork = field.type === 'networkCombo';
84150 var _isSemi = field.type === 'semiCombo';
84152 var _optstrings = field.strings && field.strings.options;
84154 var _optarray = field.options;
84156 var _snake_case = field.snake_case || field.snake_case === undefined;
84158 var _combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2);
84160 var _container = select(null);
84162 var _inputWrap = select(null);
84164 var _input = select(null);
84166 var _comboData = [];
84167 var _multiData = [];
84168 var _entityIDs = [];
84174 var _staticPlaceholder; // initialize deprecated tags array
84177 var _dataDeprecated = [];
84178 _mainFileFetcher.get('deprecated').then(function (d) {
84179 _dataDeprecated = d;
84180 })["catch"](function () {
84182 }); // ensure multiCombo field.key ends with a ':'
84184 if (_isMulti && field.key && /[^:]$/.test(field.key)) {
84188 function snake(s) {
84189 return s.replace(/\s+/g, '_');
84192 function unsnake(s) {
84193 return s.replace(/_+/g, ' ');
84196 function clean(s) {
84197 return s.split(';').map(function (s) {
84200 } // returns the tag value for a display value
84201 // (for multiCombo, dval should be the key suffix, not the entire key)
84204 function tagValue(dval) {
84205 dval = clean(dval || '');
84208 var found = _comboData.find(function (o) {
84209 return o.key && clean(o.value) === dval;
84217 if (field.type === 'typeCombo' && !dval) {
84221 return (_snake_case ? snake(dval) : dval) || undefined;
84222 } // returns the display value for a tag value
84223 // (for multiCombo, tval should be the key suffix, not the entire key)
84226 function displayValue(tval) {
84230 var found = _comboData.find(function (o) {
84231 return o.key === tval && o.value;
84235 return found.value;
84239 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
84243 return _snake_case ? unsnake(tval) : tval;
84244 } // Compute the difference between arrays of objects by `value` property
84246 // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
84247 // > [{value:1}, {value:3}]
84251 function objectDifference(a, b) {
84252 return a.filter(function (d1) {
84253 return !b.some(function (d2) {
84254 return !d2.isMixed && d1.value === d2.value;
84259 function initCombo(selection, attachTo) {
84261 selection.attr('readonly', 'readonly');
84262 selection.call(_combobox, attachTo);
84263 setStaticValues(setPlaceholder);
84264 } else if (_optarray) {
84265 selection.call(_combobox, attachTo);
84266 setStaticValues(setPlaceholder);
84267 } else if (services.taginfo) {
84268 selection.call(_combobox.fetcher(setTaginfoValues), attachTo);
84269 setTaginfoValues('', setPlaceholder);
84273 function setStaticValues(callback) {
84274 if (!(_optstrings || _optarray)) return;
84277 _comboData = Object.keys(_optstrings).map(function (k) {
84278 var v = field.t('options.' + k, {
84279 'default': _optstrings[k]
84285 display: field.t.html('options.' + k, {
84286 'default': _optstrings[k]
84290 } else if (_optarray) {
84291 _comboData = _optarray.map(function (k) {
84292 var v = _snake_case ? unsnake(k) : k;
84301 _combobox.data(objectDifference(_comboData, _multiData));
84303 if (callback) callback(_comboData);
84306 function setTaginfoValues(q, callback) {
84307 var fn = _isMulti ? 'multikeys' : 'values';
84308 var query = (_isMulti ? field.key : '') + q;
84309 var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
84311 if (hasCountryPrefix) {
84312 query = _countryCode + ':';
84316 debounce: q !== '',
84321 if (_entityIDs.length) {
84322 params.geometry = context.graph().geometry(_entityIDs[0]);
84325 services.taginfo[fn](params, function (err, data) {
84327 data = data.filter(function (d) {
84328 if (field.type === 'typeCombo' && d.value === 'yes') {
84329 // don't show the fallback value
84331 } // don't show values with very low usage
84334 return !d.count || d.count > 10;
84336 var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
84338 if (deprecatedValues) {
84339 // don't suggest deprecated tag values
84340 data = data.filter(function (d) {
84341 return deprecatedValues.indexOf(d.value) === -1;
84345 if (hasCountryPrefix) {
84346 data = data.filter(function (d) {
84347 return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
84349 } // hide the caret if there are no suggestions
84352 _container.classed('empty-combobox', data.length === 0);
84354 _comboData = data.map(function (d) {
84356 if (_isMulti) k = k.replace(field.key, '');
84357 var v = _snake_case ? unsnake(k) : k;
84361 title: _isMulti ? v : d.title
84364 _comboData = objectDifference(_comboData, _multiData);
84365 if (callback) callback(_comboData);
84369 function setPlaceholder(values) {
84370 if (_isMulti || _isSemi) {
84371 _staticPlaceholder = field.placeholder() || _t('inspector.add');
84373 var vals = values.map(function (d) {
84375 }).filter(function (s) {
84376 return s.length < 20;
84378 var placeholders = vals.length > 1 ? vals : values.map(function (d) {
84381 _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
84384 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
84385 _staticPlaceholder += '…';
84390 if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) {
84391 ph = _t('inspector.multiple_values');
84393 ph = _staticPlaceholder;
84396 _container.selectAll('input').attr('placeholder', ph);
84399 function change() {
84403 if (_isMulti || _isSemi) {
84404 val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || '';
84406 _container.classed('active', false);
84408 utilGetSetValue(_input, '');
84409 var vals = val.split(';').filter(Boolean);
84410 if (!vals.length) return;
84413 utilArrayUniq(vals).forEach(function (v) {
84414 var key = (field.key || '') + v;
84417 // don't set a multicombo value to 'yes' if it already has a non-'no' value
84418 // e.g. `language:de=main`
84419 var old = _tags[key];
84420 if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
84423 key = context.cleanTagKey(key);
84424 field.keys.push(key);
84427 } else if (_isSemi) {
84428 var arr = _multiData.map(function (d) {
84432 arr = arr.concat(vals);
84433 t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
84436 window.setTimeout(function () {
84437 _input.node().focus();
84440 var rawValue = utilGetSetValue(_input); // don't override multiple values with blank string
84442 if (!rawValue && Array.isArray(_tags[field.key])) return;
84443 val = context.cleanTagValue(tagValue(rawValue));
84444 t[field.key] = val || undefined;
84447 dispatch$1.call('change', this, t);
84450 function removeMultikey(d3_event, d) {
84451 d3_event.preventDefault();
84452 d3_event.stopPropagation();
84456 t[d.key] = undefined;
84457 } else if (_isSemi) {
84458 var arr = _multiData.map(function (md) {
84459 return md.key === d.key ? null : md.key;
84460 }).filter(Boolean);
84462 arr = utilArrayUniq(arr);
84463 t[field.key] = arr.length ? arr.join(';') : undefined;
84466 dispatch$1.call('change', this, t);
84469 function combo(selection) {
84470 _container = selection.selectAll('.form-field-input-wrap').data([0]);
84471 var type = _isMulti || _isSemi ? 'multicombo' : 'combo';
84472 _container = _container.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + type).merge(_container);
84474 if (_isMulti || _isSemi) {
84475 _container = _container.selectAll('.chiplist').data([0]);
84476 var listClass = 'chiplist'; // Use a separate line for each value in the Destinations field
84477 // to mimic highway exit signs
84479 if (field.key === 'destination') {
84480 listClass += ' full-line-chips';
84483 _container = _container.enter().append('ul').attr('class', listClass).on('click', function () {
84484 window.setTimeout(function () {
84485 _input.node().focus();
84487 }).merge(_container);
84488 _inputWrap = _container.selectAll('.input-wrap').data([0]);
84489 _inputWrap = _inputWrap.enter().append('li').attr('class', 'input-wrap').merge(_inputWrap);
84490 _input = _inputWrap.selectAll('input').data([0]);
84492 _input = _container.selectAll('input').data([0]);
84495 _input = _input.enter().append('input').attr('type', 'text').attr('id', field.domId).call(utilNoAuto).call(initCombo, selection).merge(_input);
84498 var extent = combinedEntityExtent();
84499 var countryCode = extent && iso1A2Code(extent.center());
84500 _countryCode = countryCode && countryCode.toLowerCase();
84503 _input.on('change', change).on('blur', change);
84505 _input.on('keydown.field', function (d3_event) {
84506 switch (d3_event.keyCode) {
84509 _input.node().blur(); // blurring also enters the value
84512 d3_event.stopPropagation();
84517 if (_isMulti || _isSemi) {
84518 _combobox.on('accept', function () {
84519 _input.node().blur();
84521 _input.node().focus();
84524 _input.on('focus', function () {
84525 _container.classed('active', true);
84530 combo.tags = function (tags) {
84533 if (_isMulti || _isSemi) {
84538 // Build _multiData array containing keys already set..
84539 for (var k in tags) {
84540 if (field.key && k.indexOf(field.key) !== 0 || field.keys.indexOf(k) === -1) continue;
84542 if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue;
84543 var suffix = field.key ? k.substring(field.key.length) : k;
84547 value: displayValue(suffix),
84548 isMixed: Array.isArray(v)
84553 // Set keys for form-field modified (needed for undo and reset buttons)..
84554 field.keys = _multiData.map(function (d) {
84556 }); // limit the input length so it fits after prepending the key prefix
84558 maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84560 maxLength = context.maxCharsForTagKey();
84562 } else if (_isSemi) {
84563 var allValues = [];
84566 if (Array.isArray(tags[field.key])) {
84567 tags[field.key].forEach(function (tagVal) {
84568 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84569 allValues = allValues.concat(thisVals);
84571 if (!commonValues) {
84572 commonValues = thisVals;
84574 commonValues = commonValues.filter(function (value) {
84575 return thisVals.includes(value);
84579 allValues = utilArrayUniq(allValues).filter(Boolean);
84581 allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84582 commonValues = allValues;
84585 _multiData = allValues.map(function (v) {
84588 value: displayValue(v),
84589 isMixed: !commonValues.includes(v)
84592 var currLength = utilUnicodeCharsCount(commonValues.join(';')); // limit the input length to the remaining available characters
84594 maxLength = context.maxCharsForTagValue() - currLength;
84596 if (currLength > 0) {
84597 // account for the separator if a new value will be appended to existing
84600 } // a negative maxlength doesn't make sense
84603 maxLength = Math.max(0, maxLength);
84604 var allowDragAndDrop = _isSemi // only semiCombo values are ordered
84605 && !Array.isArray(tags[field.key]); // Exclude existing multikeys from combo options..
84607 var available = objectDifference(_comboData, _multiData);
84609 _combobox.data(available); // Hide 'Add' button if this field uses fixed set of
84610 // translateable _optstrings and they're all currently used,
84611 // or if the field is already at its character limit
84614 var hideAdd = _optstrings && !available.length || maxLength <= 0;
84616 _container.selectAll('.chiplist .input-wrap').style('display', hideAdd ? 'none' : null); // Render chips
84619 var chips = _container.selectAll('.chip').data(_multiData);
84621 chips.exit().remove();
84622 var enter = chips.enter().insert('li', '.input-wrap').attr('class', 'chip');
84623 enter.append('span');
84625 chips = chips.merge(enter).order().classed('draggable', allowDragAndDrop).classed('mixed', function (d) {
84627 }).attr('title', function (d) {
84628 return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84631 if (allowDragAndDrop) {
84632 registerDragAndDrop(chips);
84635 chips.select('span').html(function (d) {
84638 chips.select('a').attr('href', '#').on('click', removeMultikey).attr('class', 'remove').html('×');
84640 var isMixed = Array.isArray(tags[field.key]);
84641 var mixedValues = isMixed && tags[field.key].map(function (val) {
84642 return displayValue(val);
84643 }).filter(Boolean);
84644 utilGetSetValue(_input, !isMixed ? displayValue(tags[field.key]) : '').attr('title', isMixed ? mixedValues.join('\n') : undefined).attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '').classed('mixed', isMixed);
84648 function registerDragAndDrop(selection) {
84649 // allow drag and drop re-ordering of chips
84650 var dragOrigin, targetIndex;
84651 selection.call(d3_drag().on('start', function (d3_event) {
84656 targetIndex = null;
84657 }).on('drag', function (d3_event) {
84658 var x = d3_event.x - dragOrigin.x,
84659 y = d3_event.y - dragOrigin.y;
84660 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
84661 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
84662 var index = selection.nodes().indexOf(this);
84663 select(this).classed('dragging', true);
84664 targetIndex = null;
84665 var targetIndexOffsetTop = null;
84666 var draggedTagWidth = select(this).node().offsetWidth;
84668 if (field.key === 'destination') {
84669 // meaning tags are full width
84670 _container.selectAll('.chip').style('transform', function (d2, index2) {
84671 var node = select(this).node();
84673 if (index === index2) {
84674 return 'translate(' + x + 'px, ' + y + 'px)'; // move the dragged tag up the order
84675 } else if (index2 > index && d3_event.y > node.offsetTop) {
84676 if (targetIndex === null || index2 > targetIndex) {
84677 targetIndex = index2;
84680 return 'translateY(-100%)'; // move the dragged tag down the order
84681 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
84682 if (targetIndex === null || index2 < targetIndex) {
84683 targetIndex = index2;
84686 return 'translateY(100%)';
84692 _container.selectAll('.chip').each(function (d2, index2) {
84693 var node = select(this).node(); // check the cursor is in the bounding box
84695 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) {
84696 targetIndex = index2;
84697 targetIndexOffsetTop = node.offsetTop;
84699 }).style('transform', function (d2, index2) {
84700 var node = select(this).node();
84702 if (index === index2) {
84703 return 'translate(' + x + 'px, ' + y + 'px)';
84704 } // only translate tags in the same row
84707 if (node.offsetTop === targetIndexOffsetTop) {
84708 if (index2 < index && index2 >= targetIndex) {
84709 return 'translateX(' + draggedTagWidth + 'px)';
84710 } else if (index2 > index && index2 <= targetIndex) {
84711 return 'translateX(-' + draggedTagWidth + 'px)';
84718 }).on('end', function () {
84719 if (!select(this).classed('dragging')) {
84723 var index = selection.nodes().indexOf(this);
84724 select(this).classed('dragging', false);
84726 _container.selectAll('.chip').style('transform', null);
84728 if (typeof targetIndex === 'number') {
84729 var element = _multiData[index];
84731 _multiData.splice(index, 1);
84733 _multiData.splice(targetIndex, 0, element);
84737 if (_multiData.length) {
84738 t[field.key] = _multiData.map(function (element) {
84739 return element.key;
84742 t[field.key] = undefined;
84745 dispatch$1.call('change', this, t);
84748 dragOrigin = undefined;
84749 targetIndex = undefined;
84753 combo.focus = function () {
84754 _input.node().focus();
84757 combo.entityIDs = function (val) {
84758 if (!arguments.length) return _entityIDs;
84763 function combinedEntityExtent() {
84764 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84767 return utilRebind(combo, dispatch$1, 'on');
84770 function uiFieldText(field, context) {
84771 var dispatch$1 = dispatch('change');
84772 var input = select(null);
84773 var outlinkButton = select(null);
84774 var _entityIDs = [];
84778 var _phoneFormats = {};
84780 if (field.type === 'tel') {
84781 _mainFileFetcher.get('phone_formats').then(function (d) {
84783 updatePhonePlaceholder();
84784 })["catch"](function () {
84789 function i(selection) {
84790 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84791 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84792 var isLocked = preset && preset.suggestion && field.id === 'brand';
84793 field.locked(isLocked);
84794 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84795 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84796 input = wrap.selectAll('input').data([0]);
84797 input = input.enter().append('input').attr('type', field.type === 'identifier' ? 'text' : field.type).attr('id', field.domId).classed(field.type, true).call(utilNoAuto).merge(input);
84798 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
84800 if (field.type === 'tel') {
84801 updatePhonePlaceholder();
84802 } else if (field.type === 'number') {
84803 var rtl = _mainLocalizer.textDirection() === 'rtl';
84804 input.attr('type', 'text');
84805 var inc = field.increment;
84806 var buttons = wrap.selectAll('.increment, .decrement').data(rtl ? [inc, -inc] : [-inc, inc]);
84807 buttons.enter().append('button').attr('class', function (d) {
84808 var which = d > 0 ? 'increment' : 'decrement';
84809 return 'form-field-button ' + which;
84810 }).merge(buttons).on('click', function (d3_event, d) {
84811 d3_event.preventDefault();
84812 var raw_vals = input.node().value || '0';
84813 var vals = raw_vals.split(';');
84814 vals = vals.map(function (v) {
84815 var num = parseFloat(v.trim(), 10);
84816 return isFinite(num) ? clamped(num + d) : v.trim();
84818 input.node().value = vals.join(';');
84821 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84822 input.attr('type', 'text');
84823 outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]);
84824 outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () {
84825 var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84827 if (domainResults.length >= 2 && domainResults[1]) {
84828 var domain = domainResults[1];
84829 return _t('icons.view_on', {
84835 }).on('click', function (d3_event) {
84836 d3_event.preventDefault();
84837 var value = validIdentifierValueForLink();
84840 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84841 window.open(url, '_blank');
84843 }).merge(outlinkButton);
84847 function updatePhonePlaceholder() {
84848 if (input.empty() || !Object.keys(_phoneFormats).length) return;
84849 var extent = combinedEntityExtent();
84850 var countryCode = extent && iso1A2Code(extent.center());
84852 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84854 if (format) input.attr('placeholder', format);
84857 function validIdentifierValueForLink() {
84858 if (field.type === 'identifier' && field.pattern) {
84859 var value = utilGetSetValue(input).trim().split(';')[0];
84860 return value && value.match(new RegExp(field.pattern));
84864 } // clamp number to min/max
84867 function clamped(num) {
84868 if (field.minValue !== undefined) {
84869 num = Math.max(num, field.minValue);
84872 if (field.maxValue !== undefined) {
84873 num = Math.min(num, field.maxValue);
84879 function change(onInput) {
84880 return function () {
84882 var val = utilGetSetValue(input);
84883 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
84885 if (!val && Array.isArray(_tags[field.key])) return;
84888 if (field.type === 'number' && val) {
84889 var vals = val.split(';');
84890 vals = vals.map(function (v) {
84891 var num = parseFloat(v.trim(), 10);
84892 return isFinite(num) ? clamped(num) : v.trim();
84894 val = vals.join(';');
84897 utilGetSetValue(input, val);
84900 t[field.key] = val || undefined;
84901 dispatch$1.call('change', this, t, onInput);
84905 i.entityIDs = function (val) {
84906 if (!arguments.length) return _entityIDs;
84911 i.tags = function (tags) {
84913 var isMixed = Array.isArray(tags[field.key]);
84914 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);
84916 if (outlinkButton && !outlinkButton.empty()) {
84917 var disabled = !validIdentifierValueForLink();
84918 outlinkButton.classed('disabled', disabled);
84922 i.focus = function () {
84923 var node = input.node();
84924 if (node) node.focus();
84927 function combinedEntityExtent() {
84928 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84931 return utilRebind(i, dispatch$1, 'on');
84934 function uiFieldAccess(field, context) {
84935 var dispatch$1 = dispatch('change');
84936 var items = select(null);
84940 function access(selection) {
84941 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84942 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84943 var list = wrap.selectAll('ul').data([0]);
84944 list = list.enter().append('ul').attr('class', 'rows').merge(list);
84945 items = list.selectAll('li').data(field.keys); // Enter
84947 var enter = items.enter().append('li').attr('class', function (d) {
84948 return 'labeled-input preset-access-' + d;
84950 enter.append('span').attr('class', 'label preset-label-access').attr('for', function (d) {
84951 return 'preset-input-access-' + d;
84952 }).html(function (d) {
84953 return field.t.html('types.' + d);
84955 enter.append('div').attr('class', 'preset-input-access-wrap').append('input').attr('type', 'text').attr('class', function (d) {
84956 return 'preset-input-access preset-input-access-' + d;
84957 }).call(utilNoAuto).each(function (d) {
84958 select(this).call(uiCombobox(context, 'access-' + d).data(access.options(d)));
84961 items = items.merge(enter);
84962 wrap.selectAll('.preset-input-access').on('change', change).on('blur', change);
84965 function change(d3_event, d) {
84967 var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
84969 if (!value && typeof _tags[d] !== 'string') return;
84970 tag[d] = value || undefined;
84971 dispatch$1.call('change', this, tag);
84974 access.options = function (type) {
84975 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
84977 if (type !== 'access') {
84978 options.unshift('yes');
84979 options.push('designated');
84981 if (type === 'bicycle') {
84982 options.push('dismount');
84986 return options.map(function (option) {
84988 title: field.t('options.' + option + '.description'),
84994 var placeholdersByHighway = {
84996 foot: 'designated',
84997 motor_vehicle: 'no'
85001 motor_vehicle: 'no',
85007 motor_vehicle: 'no'
85010 motor_vehicle: 'no',
85011 bicycle: 'designated'
85014 motor_vehicle: 'no',
85015 horse: 'designated'
85019 motor_vehicle: 'no',
85025 motor_vehicle: 'yes',
85030 motor_vehicle: 'yes'
85034 motor_vehicle: 'yes',
85040 motor_vehicle: 'yes',
85046 motor_vehicle: 'yes',
85052 motor_vehicle: 'yes',
85058 motor_vehicle: 'yes',
85064 motor_vehicle: 'yes',
85070 motor_vehicle: 'yes',
85075 motor_vehicle: 'yes'
85079 motor_vehicle: 'yes',
85085 motor_vehicle: 'yes',
85091 motor_vehicle: 'yes',
85097 access.tags = function (tags) {
85099 utilGetSetValue(items.selectAll('.preset-input-access'), function (d) {
85100 return typeof tags[d] === 'string' ? tags[d] : '';
85101 }).classed('mixed', function (d) {
85102 return tags[d] && Array.isArray(tags[d]);
85103 }).attr('title', function (d) {
85104 return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
85105 }).attr('placeholder', function (d) {
85106 if (tags[d] && Array.isArray(tags[d])) {
85107 return _t('inspector.multiple_values');
85110 if (d === 'access') {
85114 if (tags.access && typeof tags.access === 'string') {
85115 return tags.access;
85118 if (tags.highway) {
85119 if (typeof tags.highway === 'string') {
85120 if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) {
85121 return placeholdersByHighway[tags.highway][d];
85124 var impliedAccesses = tags.highway.filter(Boolean).map(function (highwayVal) {
85125 return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
85126 }).filter(Boolean);
85128 if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) {
85129 // if all the highway values have the same implied access for this type then use that
85130 return impliedAccesses[0];
85135 return field.placeholder();
85139 access.focus = function () {
85140 items.selectAll('.preset-input-access').node().focus();
85143 return utilRebind(access, dispatch$1, 'on');
85146 function uiFieldAddress(field, context) {
85147 var dispatch$1 = dispatch('change');
85149 var _selection = select(null);
85151 var _wrap = select(null);
85153 var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings
85155 var _entityIDs = [];
85161 var _addressFormats = [{
85162 format: [['housenumber', 'street'], ['city', 'postcode']]
85164 _mainFileFetcher.get('address_formats').then(function (d) {
85165 _addressFormats = d;
85167 if (!_selection.empty()) {
85168 _selection.call(address);
85170 })["catch"](function () {
85174 function getNearStreets() {
85175 var extent = combinedEntityExtent();
85176 var l = extent.center();
85177 var box = geoExtent(l).padByMeters(200);
85178 var streets = context.history().intersects(box).filter(isAddressable).map(function (d) {
85179 var loc = context.projection([(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2]);
85180 var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
85182 title: d.tags.name,
85183 value: d.tags.name,
85184 dist: choice.distance
85186 }).sort(function (a, b) {
85187 return a.dist - b.dist;
85189 return utilArrayUniqBy(streets, 'value');
85191 function isAddressable(d) {
85192 return d.tags.highway && d.tags.name && d.type === 'way';
85196 function getNearCities() {
85197 var extent = combinedEntityExtent();
85198 var l = extent.center();
85199 var box = geoExtent(l).padByMeters(200);
85200 var cities = context.history().intersects(box).filter(isAddressable).map(function (d) {
85202 title: d.tags['addr:city'] || d.tags.name,
85203 value: d.tags['addr:city'] || d.tags.name,
85204 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
85206 }).sort(function (a, b) {
85207 return a.dist - b.dist;
85209 return utilArrayUniqBy(cities, 'value');
85211 function isAddressable(d) {
85213 if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true;
85214 if (d.tags.border_type === 'city') return true;
85215 if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true;
85218 if (d.tags['addr:city']) return true;
85223 function getNearValues(key) {
85224 var extent = combinedEntityExtent();
85225 var l = extent.center();
85226 var box = geoExtent(l).padByMeters(200);
85227 var results = context.history().intersects(box).filter(function hasTag(d) {
85228 return _entityIDs.indexOf(d.id) === -1 && d.tags[key];
85229 }).map(function (d) {
85231 title: d.tags[key],
85232 value: d.tags[key],
85233 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
85235 }).sort(function (a, b) {
85236 return a.dist - b.dist;
85238 return utilArrayUniqBy(results, 'value');
85241 function updateForCountryCode() {
85242 if (!_countryCode) return;
85245 for (var i = 0; i < _addressFormats.length; i++) {
85246 var format = _addressFormats[i];
85248 if (!format.countryCodes) {
85249 addressFormat = format; // choose the default format, keep going
85250 } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
85251 addressFormat = format; // choose the country format, stop here
85257 var dropdowns = addressFormat.dropdowns || ['city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', 'quarter', 'state', 'street', 'subdistrict', 'suburb'];
85258 var widths = addressFormat.widths || {
85259 housenumber: 1 / 3,
85267 // Normalize widths.
85268 var total = r.reduce(function (sum, key) {
85269 return sum + (widths[key] || 0.5);
85271 return r.map(function (key) {
85274 width: (widths[key] || 0.5) / total
85279 var rows = _wrap.selectAll('.addr-row').data(addressFormat.format, function (d) {
85280 return d.toString();
85283 rows.exit().remove();
85284 rows.enter().append('div').attr('class', 'addr-row').selectAll('input').data(row).enter().append('input').property('type', 'text').call(updatePlaceholder).attr('class', function (d) {
85285 return 'addr-' + d.id;
85286 }).call(utilNoAuto).each(addDropdown).style('width', function (d) {
85287 return d.width * 100 + '%';
85290 function addDropdown(d) {
85291 if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown
85293 var nearValues = d.id === 'street' ? getNearStreets : d.id === 'city' ? getNearCities : getNearValues;
85294 select(this).call(uiCombobox(context, 'address-' + d.id).minItems(1).caseSensitive(true).fetcher(function (value, callback) {
85295 callback(nearValues('addr:' + d.id));
85299 _wrap.selectAll('input').on('blur', change()).on('change', change());
85301 _wrap.selectAll('input:not(.combobox-input)').on('input', change(true));
85303 if (_tags) updateTags(_tags);
85306 function address(selection) {
85307 _selection = selection;
85308 _wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85309 _wrap = _wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(_wrap);
85310 var extent = combinedEntityExtent();
85315 if (context.inIntro()) {
85316 // localize the address format for the walkthrough
85317 countryCode = _t('intro.graph.countrycode');
85319 var center = extent.center();
85320 countryCode = iso1A2Code(center);
85324 _countryCode = countryCode.toLowerCase();
85325 updateForCountryCode();
85330 function change(onInput) {
85331 return function () {
85334 _wrap.selectAll('input').each(function (subfield) {
85335 var key = field.key + ':' + subfield.id;
85336 var value = this.value;
85337 if (!onInput) value = context.cleanTagValue(value); // don't override multiple values with blank string
85339 if (Array.isArray(_tags[key]) && !value) return;
85340 tags[key] = value || undefined;
85343 dispatch$1.call('change', this, tags, onInput);
85347 function updatePlaceholder(inputSelection) {
85348 return inputSelection.attr('placeholder', function (subfield) {
85349 if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85350 return _t('inspector.multiple_values');
85353 if (_countryCode) {
85354 var localkey = subfield.id + '!' + _countryCode;
85355 var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85356 return addrField.t('placeholders.' + tkey);
85361 function updateTags(tags) {
85362 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85363 var val = tags[field.key + ':' + subfield.id];
85364 return typeof val === 'string' ? val : '';
85365 }).attr('title', function (subfield) {
85366 var val = tags[field.key + ':' + subfield.id];
85367 return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85368 }).classed('mixed', function (subfield) {
85369 return Array.isArray(tags[field.key + ':' + subfield.id]);
85370 }).call(updatePlaceholder);
85373 function combinedEntityExtent() {
85374 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85377 address.entityIDs = function (val) {
85378 if (!arguments.length) return _entityIDs;
85383 address.tags = function (tags) {
85388 address.focus = function () {
85389 var node = _wrap.selectAll('input').node();
85391 if (node) node.focus();
85394 return utilRebind(address, dispatch$1, 'on');
85397 function uiFieldCycleway(field, context) {
85398 var dispatch$1 = dispatch('change');
85399 var items = select(null);
85400 var wrap = select(null);
85404 function cycleway(selection) {
85405 function stripcolon(s) {
85406 return s.replace(':', '');
85409 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85410 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85411 var div = wrap.selectAll('ul').data([0]);
85412 div = div.enter().append('ul').attr('class', 'rows').merge(div);
85413 var keys = ['cycleway:left', 'cycleway:right'];
85414 items = div.selectAll('li').data(keys);
85415 var enter = items.enter().append('li').attr('class', function (d) {
85416 return 'labeled-input preset-cycleway-' + stripcolon(d);
85418 enter.append('span').attr('class', 'label preset-label-cycleway').attr('for', function (d) {
85419 return 'preset-input-cycleway-' + stripcolon(d);
85420 }).html(function (d) {
85421 return field.t.html('types.' + d);
85423 enter.append('div').attr('class', 'preset-input-cycleway-wrap').append('input').attr('type', 'text').attr('class', function (d) {
85424 return 'preset-input-cycleway preset-input-' + stripcolon(d);
85425 }).call(utilNoAuto).each(function (d) {
85426 select(this).call(uiCombobox(context, 'cycleway-' + stripcolon(d)).data(cycleway.options(d)));
85428 items = items.merge(enter); // Update
85430 wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change);
85433 function change(d3_event, key) {
85434 var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
85436 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
85438 if (newValue === 'none' || newValue === '') {
85439 newValue = undefined;
85442 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85443 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85445 if (otherValue && Array.isArray(otherValue)) {
85446 // we must always have an explicit value for comparison
85447 otherValue = otherValue[0];
85450 if (otherValue === 'none' || otherValue === '') {
85451 otherValue = undefined;
85454 var tag = {}; // If the left and right tags match, use the cycleway tag to tag both
85455 // sides the same way
85457 if (newValue === otherValue) {
85459 cycleway: newValue,
85460 'cycleway:left': undefined,
85461 'cycleway:right': undefined
85464 // Always set both left and right as changing one can affect the other
85466 cycleway: undefined
85468 tag[key] = newValue;
85469 tag[otherKey] = otherValue;
85472 dispatch$1.call('change', this, tag);
85475 cycleway.options = function () {
85476 return Object.keys(field.strings.options).map(function (option) {
85478 title: field.t('options.' + option + '.description'),
85484 cycleway.tags = function (tags) {
85485 _tags = tags; // If cycleway is set, use that instead of individual values
85487 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85488 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function (d) {
85489 if (commonValue) return commonValue;
85490 return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85491 }).attr('title', function (d) {
85492 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85495 if (Array.isArray(tags.cycleway)) {
85496 vals = vals.concat(tags.cycleway);
85499 if (Array.isArray(tags[d])) {
85500 vals = vals.concat(tags[d]);
85503 return vals.filter(Boolean).join('\n');
85507 }).attr('placeholder', function (d) {
85508 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85509 return _t('inspector.multiple_values');
85512 return field.placeholder();
85513 }).classed('mixed', function (d) {
85514 return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85518 cycleway.focus = function () {
85519 var node = wrap.selectAll('input').node();
85520 if (node) node.focus();
85523 return utilRebind(cycleway, dispatch$1, 'on');
85526 function uiFieldLanes(field, context) {
85527 var dispatch$1 = dispatch('change');
85528 var LANE_WIDTH = 40;
85529 var LANE_HEIGHT = 200;
85530 var _entityIDs = [];
85532 function lanes(selection) {
85533 var lanesData = context.entity(_entityIDs[0]).lanes();
85535 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85536 selection.call(lanes.off);
85540 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85541 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85542 var surface = wrap.selectAll('.surface').data([0]);
85543 var d = utilGetDimensions(wrap);
85544 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85545 surface = surface.enter().append('svg').attr('width', d[0]).attr('height', 300).attr('class', 'surface').merge(surface);
85546 var lanesSelection = surface.selectAll('.lanes').data([0]);
85547 lanesSelection = lanesSelection.enter().append('g').attr('class', 'lanes').merge(lanesSelection);
85548 lanesSelection.attr('transform', function () {
85549 return 'translate(' + freeSpace / 2 + ', 0)';
85551 var lane = lanesSelection.selectAll('.lane').data(lanesData.lanes);
85552 lane.exit().remove();
85553 var enter = lane.enter().append('g').attr('class', 'lane');
85554 enter.append('g').append('rect').attr('y', 50).attr('width', LANE_WIDTH).attr('height', LANE_HEIGHT);
85555 enter.append('g').attr('class', 'forward').append('text').attr('y', 40).attr('x', 14).html('▲');
85556 enter.append('g').attr('class', 'bothways').append('text').attr('y', 40).attr('x', 14).html('▲▼');
85557 enter.append('g').attr('class', 'backward').append('text').attr('y', 40).attr('x', 14).html('▼');
85558 lane = lane.merge(enter);
85559 lane.attr('transform', function (d) {
85560 return 'translate(' + LANE_WIDTH * d.index * 1.5 + ', 0)';
85562 lane.select('.forward').style('visibility', function (d) {
85563 return d.direction === 'forward' ? 'visible' : 'hidden';
85565 lane.select('.bothways').style('visibility', function (d) {
85566 return d.direction === 'bothways' ? 'visible' : 'hidden';
85568 lane.select('.backward').style('visibility', function (d) {
85569 return d.direction === 'backward' ? 'visible' : 'hidden';
85573 lanes.entityIDs = function (val) {
85577 lanes.tags = function () {};
85579 lanes.focus = function () {};
85581 lanes.off = function () {};
85583 return utilRebind(lanes, dispatch$1, 'on');
85585 uiFieldLanes.supportsMultiselection = false;
85587 var _languagesArray = [];
85588 function uiFieldLocalized(field, context) {
85589 var dispatch$1 = dispatch('change', 'input');
85590 var wikipedia = services.wikipedia;
85591 var input = select(null);
85592 var localizedInputs = select(null);
85596 var _tags; // A concern here in switching to async data means that _languagesArray will not
85597 // be available the first time through, so things like the fetchers and
85598 // the language() function will not work immediately.
85601 _mainFileFetcher.get('languages').then(loadLanguagesArray)["catch"](function () {
85604 var _territoryLanguages = {};
85605 _mainFileFetcher.get('territory_languages').then(function (d) {
85606 _territoryLanguages = d;
85607 })["catch"](function () {
85610 var allSuggestions = _mainPresetIndex.collection.filter(function (p) {
85611 return p.suggestion === true;
85612 }); // reuse these combos
85614 var langCombo = uiCombobox(context, 'localized-lang').fetcher(fetchLanguages).minItems(0);
85615 var brandCombo = uiCombobox(context, 'localized-brand').canAutocomplete(false).minItems(1);
85617 var _selection = select(null);
85619 var _multilingual = [];
85621 var _buttonTip = uiTooltip().title(_t.html('translate.translate')).placement('left');
85625 var _entityIDs = [];
85627 function loadLanguagesArray(dataLanguages) {
85628 if (_languagesArray.length !== 0) return; // some conversion is needed to ensure correct OSM tags are used
85630 var replacements = {
85632 // in OSM, `sr` implies Cyrillic
85633 'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM
85637 for (var code in dataLanguages) {
85638 if (replacements[code] === false) continue;
85639 var metaCode = code;
85640 if (replacements[code]) metaCode = replacements[code];
85642 _languagesArray.push({
85643 localName: _mainLocalizer.languageName(metaCode, {
85646 nativeName: dataLanguages[metaCode].nativeName,
85648 label: _mainLocalizer.languageName(metaCode)
85653 function calcLocked() {
85654 // only lock the Name field
85655 var isLocked = field.id === 'name' && _entityIDs.length && // lock the field if any feature needs it
85656 _entityIDs.some(function (entityID) {
85657 var entity = context.graph().hasEntity(entityID);
85658 if (!entity) return false;
85660 var original = context.graph().base().entities[_entityIDs[0]];
85662 var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name; // if the name was already edited manually then allow further editing
85664 if (!hasOriginalName) return false; // features linked to Wikidata are likely important and should be protected
85666 if (entity.tags.wikidata) return true; // assume the name has already been confirmed if its source has been researched
85668 if (entity.tags['name:etymology:wikidata']) return true;
85669 var preset = _mainPresetIndex.match(entity, context.graph());
85670 var isSuggestion = preset && preset.suggestion;
85671 var showsBrand = preset && preset.originalFields.filter(function (d) {
85672 return d.id === 'brand';
85673 }).length; // protect standardized brand names
85675 return isSuggestion && !showsBrand;
85678 field.locked(isLocked);
85679 } // update _multilingual, maintaining the existing order
85682 function calcMultilingual(tags) {
85683 var existingLangsOrdered = _multilingual.map(function (item) {
85687 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85689 for (var k in tags) {
85690 var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
85692 if (m && m[1] === field.key && m[2]) {
85698 if (existingLangs.has(item.lang)) {
85699 // update the value
85700 _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85701 existingLangs["delete"](item.lang);
85703 _multilingual.push(item);
85708 _multilingual = _multilingual.filter(function (item) {
85709 return !item.lang || !existingLangs.has(item.lang);
85713 function localized(selection) {
85714 _selection = selection;
85716 var isLocked = field.locked();
85717 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85718 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85719 var wrap = selection.selectAll('.form-field-input-wrap').data([0]); // enter/update
85721 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85722 input = wrap.selectAll('.localized-main').data([0]); // enter/update
85724 input = input.enter().append('input').attr('type', 'text').attr('id', field.domId).attr('class', 'localized-main').call(utilNoAuto).merge(input);
85726 if (preset && field.id === 'name') {
85727 var pTag = preset.id.split('/', 2);
85728 var pKey = pTag[0];
85729 var pValue = pTag[1];
85731 if (!preset.suggestion) {
85732 // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85733 // This code attempts to determine if the matched preset is the
85734 // kind of preset that even can benefit from name suggestions..
85735 // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85736 // - false = churches, parks, hospitals, etc. (things not in the index)
85737 var isFallback = preset.isFallback();
85738 var goodSuggestions = allSuggestions.filter(function (s) {
85739 if (isFallback) return true;
85740 var sTag = s.id.split('/', 2);
85741 var sKey = sTag[0];
85742 var sValue = sTag[1];
85743 return pKey === sKey && (!pValue || pValue === sValue);
85744 }); // Show the suggestions.. If the user picks one, change the tags..
85746 if (allSuggestions.length && goodSuggestions.length) {
85747 input.on('blur.localized', checkBrandOnBlur).call(brandCombo.fetcher(fetchBrandNames(preset, allSuggestions)).on('accept', acceptBrand).on('cancel', cancelBrand));
85752 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
85753 var translateButton = wrap.selectAll('.localized-add').data([0]);
85754 translateButton = translateButton.enter().append('button').attr('class', 'localized-add form-field-button').call(svgIcon('#iD-icon-plus')).merge(translateButton);
85755 translateButton.classed('disabled', !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on('click', addNew);
85757 if (_tags && !_multilingual.length) {
85758 calcMultilingual(_tags);
85761 localizedInputs = selection.selectAll('.localized-multilingual').data([0]);
85762 localizedInputs = localizedInputs.enter().append('div').attr('class', 'localized-multilingual').merge(localizedInputs);
85763 localizedInputs.call(renderMultilingual);
85764 localizedInputs.selectAll('button, input').classed('disabled', !!isLocked).attr('readonly', isLocked || null); // We are not guaranteed to get an `accept` or `cancel` when blurring the field.
85765 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85766 // So compare the current field value against the suggestions one last time.
85768 function checkBrandOnBlur() {
85769 var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85770 if (!latest) return; // deleting the entity blurred the field?
85772 var preset = _mainPresetIndex.match(latest, context.graph());
85773 if (preset && preset.suggestion) return; // already accepted
85775 var name = utilGetSetValue(input).trim();
85776 var matched = allSuggestions.filter(function (s) {
85777 return name === s.name();
85780 if (matched.length === 1) {
85782 suggestion: matched[0]
85789 function acceptBrand(d) {
85790 var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85792 if (!d || !entity) {
85797 var tags = entity.tags;
85798 var geometry = entity.geometry(context.graph());
85799 var removed = preset.unsetTags(tags, geometry);
85801 for (var k in tags) {
85802 tags[k] = removed[k]; // set removed tags to `undefined`
85805 tags = d.suggestion.setTags(tags, geometry);
85806 utilGetSetValue(input, tags.name);
85807 dispatch$1.call('change', this, tags);
85808 } // user hit escape
85811 function cancelBrand() {
85812 var name = utilGetSetValue(input);
85813 dispatch$1.call('change', this, {
85818 function fetchBrandNames(preset, suggestions) {
85819 var pTag = preset.id.split('/', 2);
85820 var pKey = pTag[0];
85821 var pValue = pTag[1];
85822 return function (value, callback) {
85825 if (value && value.length > 2) {
85826 for (var i = 0; i < suggestions.length; i++) {
85827 var s = suggestions[i]; // don't suggest brands from incompatible countries
85829 if (_countryCode && s.countryCodes && s.countryCodes.indexOf(_countryCode) === -1) continue;
85830 var sTag = s.id.split('/', 2);
85831 var sKey = sTag[0];
85832 var sValue = sTag[1];
85833 var subtitle = s.subtitle();
85834 var name = s.name();
85835 if (subtitle) name += ' – ' + subtitle;
85836 var dist = utilEditDistance(value, name.substring(0, value.length));
85837 var matchesPreset = pKey === sKey && (!pValue || pValue === sValue);
85839 if (dist < 1 || matchesPreset && dist < 3) {
85843 display: s.nameLabel() + (subtitle ? ' – ' + s.subtitleLabel() : ''),
85845 dist: dist + (matchesPreset ? 0 : 1) // penalize if not matched preset
85852 results.sort(function (a, b) {
85853 return a.dist - b.dist;
85857 results = results.slice(0, 10);
85862 function addNew(d3_event) {
85863 d3_event.preventDefault();
85864 if (field.locked()) return;
85865 var defaultLang = _mainLocalizer.languageCode().toLowerCase();
85867 var langExists = _multilingual.find(function (datum) {
85868 return datum.lang === defaultLang;
85871 var isLangEn = defaultLang.indexOf('en') > -1;
85873 if (isLangEn || langExists) {
85875 langExists = _multilingual.find(function (datum) {
85876 return datum.lang === defaultLang;
85881 // prepend the value so it appears at the top
85882 _multilingual.unshift({
85887 localizedInputs.call(renderMultilingual);
85891 function change(onInput) {
85892 return function (d3_event) {
85893 if (field.locked()) {
85894 d3_event.preventDefault();
85898 var val = utilGetSetValue(select(this));
85899 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
85901 if (!val && Array.isArray(_tags[field.key])) return;
85903 t[field.key] = val || undefined;
85904 dispatch$1.call('change', this, t, onInput);
85909 function key(lang) {
85910 return field.key + ':' + lang;
85913 function changeLang(d3_event, d) {
85914 var tags = {}; // make sure unrecognized suffixes are lowercase - #7156
85916 var lang = utilGetSetValue(select(this)).toLowerCase();
85918 var language = _languagesArray.find(function (d) {
85919 return d.label.toLowerCase() === lang || d.localName && d.localName.toLowerCase() === lang || d.nativeName && d.nativeName.toLowerCase() === lang;
85922 if (language) lang = language.code;
85924 if (d.lang && d.lang !== lang) {
85925 tags[key(d.lang)] = undefined;
85928 var newKey = lang && context.cleanTagKey(key(lang));
85929 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
85931 if (newKey && value) {
85932 tags[newKey] = value;
85933 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
85934 tags[newKey] = _wikiTitles[d.lang];
85938 dispatch$1.call('change', this, tags);
85941 function changeValue(d3_event, d) {
85942 if (!d.lang) return;
85943 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; // don't override multiple values with blank string
85945 if (!value && Array.isArray(d.value)) return;
85947 t[key(d.lang)] = value;
85949 dispatch$1.call('change', this, t);
85952 function fetchLanguages(value, cb) {
85953 var v = value.toLowerCase(); // show the user's language first
85955 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
85957 if (_countryCode && _territoryLanguages[_countryCode]) {
85958 langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
85961 var langItems = [];
85962 langCodes.forEach(function (code) {
85963 var langItem = _languagesArray.find(function (item) {
85964 return item.code === code;
85967 if (langItem) langItems.push(langItem);
85969 langItems = utilArrayUniq(langItems.concat(_languagesArray));
85970 cb(langItems.filter(function (d) {
85971 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;
85972 }).map(function (d) {
85979 function renderMultilingual(selection) {
85980 var entries = selection.selectAll('div.entry').data(_multilingual, function (d) {
85983 entries.exit().style('top', '0').style('max-height', '240px').transition().duration(200).style('opacity', '0').style('max-height', '0px').remove();
85984 var entriesEnter = entries.enter().append('div').attr('class', 'entry').each(function (_, index) {
85985 var wrap = select(this);
85986 var domId = utilUniqueDomId(index);
85987 var label = wrap.append('label').attr('class', 'field-label').attr('for', domId);
85988 var text = label.append('span').attr('class', 'label-text');
85989 text.append('span').attr('class', 'label-textvalue').html(_t.html('translate.localized_translation_label'));
85990 text.append('span').attr('class', 'label-textannotation');
85991 label.append('button').attr('class', 'remove-icon-multilingual').on('click', function (d3_event, d) {
85992 if (field.locked()) return;
85993 d3_event.preventDefault();
85995 if (!d.lang || !d.value) {
85996 _multilingual.splice(index, 1);
85998 renderMultilingual(selection);
86000 // remove from entity tags
86002 t[key(d.lang)] = undefined;
86003 dispatch$1.call('change', this, t);
86005 }).call(svgIcon('#iD-operation-delete'));
86006 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);
86007 wrap.append('input').attr('type', 'text').attr('class', 'localized-value').on('blur', changeValue).on('change', changeValue);
86009 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 () {
86010 select(this).style('max-height', '').style('overflow', 'visible');
86012 entries = entries.merge(entriesEnter);
86014 entries.classed('present', function (d) {
86015 return d.lang && d.value;
86017 utilGetSetValue(entries.select('.localized-lang'), function (d) {
86018 return _mainLocalizer.languageName(d.lang);
86020 utilGetSetValue(entries.select('.localized-value'), function (d) {
86021 return typeof d.value === 'string' ? d.value : '';
86022 }).attr('title', function (d) {
86023 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
86024 }).attr('placeholder', function (d) {
86025 return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
86026 }).classed('mixed', function (d) {
86027 return Array.isArray(d.value);
86031 localized.tags = function (tags) {
86032 _tags = tags; // Fetch translations from wikipedia
86034 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
86036 var wm = tags.wikipedia.match(/([^:]+):(.+)/);
86038 if (wm && wm[0] && wm[1]) {
86039 wikipedia.translations(wm[1], wm[2], function (err, d) {
86040 if (err || !d) return;
86046 var isMixed = Array.isArray(tags[field.key]);
86047 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);
86048 calcMultilingual(tags);
86050 _selection.call(localized);
86053 localized.focus = function () {
86054 input.node().focus();
86057 localized.entityIDs = function (val) {
86058 if (!arguments.length) return _entityIDs;
86060 _multilingual = [];
86065 function loadCountryCode() {
86066 var extent = combinedEntityExtent();
86067 var countryCode = extent && iso1A2Code(extent.center());
86068 _countryCode = countryCode && countryCode.toLowerCase();
86071 function combinedEntityExtent() {
86072 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86075 return utilRebind(localized, dispatch$1, 'on');
86078 function uiFieldMaxspeed(field, context) {
86079 var dispatch$1 = dispatch('change');
86080 var unitInput = select(null);
86081 var input = select(null);
86082 var _entityIDs = [];
86088 var speedCombo = uiCombobox(context, 'maxspeed');
86089 var unitCombo = uiCombobox(context, 'maxspeed-unit').data(['km/h', 'mph'].map(comboValues));
86090 var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86091 var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86093 function maxspeed(selection) {
86094 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86095 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86096 input = wrap.selectAll('input.maxspeed-number').data([0]);
86097 input = input.enter().append('input').attr('type', 'text').attr('class', 'maxspeed-number').attr('id', field.domId).call(utilNoAuto).call(speedCombo).merge(input);
86098 input.on('change', change).on('blur', change);
86099 var loc = combinedEntityExtent().center();
86100 _isImperial = roadSpeedUnit(loc) === 'mph';
86101 unitInput = wrap.selectAll('input.maxspeed-unit').data([0]);
86102 unitInput = unitInput.enter().append('input').attr('type', 'text').attr('class', 'maxspeed-unit').call(unitCombo).merge(unitInput);
86103 unitInput.on('blur', changeUnits).on('change', changeUnits);
86105 function changeUnits() {
86106 _isImperial = utilGetSetValue(unitInput) === 'mph';
86107 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86108 setUnitSuggestions();
86113 function setUnitSuggestions() {
86114 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86115 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86118 function comboValues(d) {
86120 value: d.toString(),
86121 title: d.toString()
86125 function change() {
86127 var value = utilGetSetValue(input).trim(); // don't override multiple values with blank string
86129 if (!value && Array.isArray(_tags[field.key])) return;
86132 tag[field.key] = undefined;
86133 } else if (isNaN(value) || !_isImperial) {
86134 tag[field.key] = context.cleanTagValue(value);
86136 tag[field.key] = context.cleanTagValue(value + ' mph');
86139 dispatch$1.call('change', this, tag);
86142 maxspeed.tags = function (tags) {
86144 var value = tags[field.key];
86145 var isMixed = Array.isArray(value);
86148 if (value && value.indexOf('mph') >= 0) {
86149 value = parseInt(value, 10).toString();
86150 _isImperial = true;
86151 } else if (value) {
86152 _isImperial = false;
86156 setUnitSuggestions();
86157 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);
86160 maxspeed.focus = function () {
86161 input.node().focus();
86164 maxspeed.entityIDs = function (val) {
86168 function combinedEntityExtent() {
86169 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86172 return utilRebind(maxspeed, dispatch$1, 'on');
86175 function uiFieldRadio(field, context) {
86176 var dispatch$1 = dispatch('change');
86177 var placeholder = select(null);
86178 var wrap = select(null);
86179 var labels = select(null);
86180 var radios = select(null);
86181 var radioData = (field.options || field.strings && field.strings.options && Object.keys(field.strings.options) || field.keys).slice(); // shallow copy
86186 var _entityIDs = [];
86188 function selectedKey() {
86189 var node = wrap.selectAll('.form-field-input-radio label.active input');
86190 return !node.empty() && node.datum();
86193 function radio(selection) {
86194 selection.classed('preset-radio', true);
86195 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86196 var enter = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-radio');
86197 enter.append('span').attr('class', 'placeholder');
86198 wrap = wrap.merge(enter);
86199 placeholder = wrap.selectAll('.placeholder');
86200 labels = wrap.selectAll('label').data(radioData);
86201 enter = labels.enter().append('label');
86202 enter.append('input').attr('type', 'radio').attr('name', field.id).attr('value', function (d) {
86203 return field.t('options.' + d, {
86206 }).attr('checked', false);
86207 enter.append('span').html(function (d) {
86208 return field.t.html('options.' + d, {
86212 labels = labels.merge(enter);
86213 radios = labels.selectAll('input').on('change', changeRadio);
86216 function structureExtras(selection, tags) {
86217 var selected = selectedKey() || tags.layer !== undefined;
86218 var type = _mainPresetIndex.field(selected);
86219 var layer = _mainPresetIndex.field('layer');
86220 var showLayer = selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined;
86221 var extrasWrap = selection.selectAll('.structure-extras-wrap').data(selected ? [0] : []);
86222 extrasWrap.exit().remove();
86223 extrasWrap = extrasWrap.enter().append('div').attr('class', 'structure-extras-wrap').merge(extrasWrap);
86224 var list = extrasWrap.selectAll('ul').data([0]);
86225 list = list.enter().append('ul').attr('class', 'rows').merge(list); // Type
86228 if (!typeField || typeField.id !== selected) {
86229 typeField = uiField(context, type, _entityIDs, {
86231 }).on('change', changeType);
86234 typeField.tags(tags);
86239 var typeItem = list.selectAll('.structure-type-item').data(typeField ? [typeField] : [], function (d) {
86243 typeItem.exit().remove(); // Enter
86245 var typeEnter = typeItem.enter().insert('li', ':first-child').attr('class', 'labeled-input structure-type-item');
86246 typeEnter.append('span').attr('class', 'label structure-label-type').attr('for', 'preset-input-' + selected).html(_t.html('inspector.radio.structure.type'));
86247 typeEnter.append('div').attr('class', 'structure-input-type-wrap'); // Update
86249 typeItem = typeItem.merge(typeEnter);
86252 typeItem.selectAll('.structure-input-type-wrap').call(typeField.render);
86256 if (layer && showLayer) {
86258 layerField = uiField(context, layer, _entityIDs, {
86260 }).on('change', changeLayer);
86263 layerField.tags(tags);
86264 field.keys = utilArrayUnion(field.keys, ['layer']);
86267 field.keys = field.keys.filter(function (k) {
86268 return k !== 'layer';
86272 var layerItem = list.selectAll('.structure-layer-item').data(layerField ? [layerField] : []); // Exit
86274 layerItem.exit().remove(); // Enter
86276 var layerEnter = layerItem.enter().append('li').attr('class', 'labeled-input structure-layer-item');
86277 layerEnter.append('span').attr('class', 'label structure-label-layer').attr('for', 'preset-input-layer').html(_t.html('inspector.radio.structure.layer'));
86278 layerEnter.append('div').attr('class', 'structure-input-layer-wrap'); // Update
86280 layerItem = layerItem.merge(layerEnter);
86283 layerItem.selectAll('.structure-input-layer-wrap').call(layerField.render);
86287 function changeType(t, onInput) {
86288 var key = selectedKey();
86292 if (val !== 'no') {
86293 _oldType[key] = val;
86296 if (field.type === 'structureRadio') {
86297 // remove layer if it should not be set
86298 if (val === 'no' || key !== 'bridge' && key !== 'tunnel' || key === 'tunnel' && val === 'building_passage') {
86299 t.layer = undefined;
86300 } // add layer if it should be set
86303 if (t.layer === undefined) {
86304 if (key === 'bridge' && val !== 'no') {
86308 if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86314 dispatch$1.call('change', this, t, onInput);
86317 function changeLayer(t, onInput) {
86318 if (t.layer === '0') {
86319 t.layer = undefined;
86322 dispatch$1.call('change', this, t, onInput);
86325 function changeRadio() {
86330 t[field.key] = undefined;
86333 radios.each(function (d) {
86334 var active = select(this).property('checked');
86335 if (active) activeKey = d;
86338 if (active) t[field.key] = d;
86340 var val = _oldType[activeKey] || 'yes';
86341 t[d] = active ? val : undefined;
86345 if (field.type === 'structureRadio') {
86346 if (activeKey === 'bridge') {
86348 } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86351 t.layer = undefined;
86355 dispatch$1.call('change', this, t);
86358 radio.tags = function (tags) {
86359 radios.property('checked', function (d) {
86361 return tags[field.key] === d;
86364 return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86367 function isMixed(d) {
86369 return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86372 return Array.isArray(tags[d]);
86375 labels.classed('active', function (d) {
86377 return Array.isArray(tags[field.key]) && tags[field.key].includes(d) || tags[field.key] === d;
86380 return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86381 }).classed('mixed', isMixed).attr('title', function (d) {
86382 return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86384 var selection = radios.filter(function () {
86385 return this.checked;
86388 if (selection.empty()) {
86389 placeholder.html(_t.html('inspector.none'));
86391 placeholder.html(selection.attr('value'));
86392 _oldType[selection.datum()] = tags[selection.datum()];
86395 if (field.type === 'structureRadio') {
86396 // For waterways without a tunnel tag, set 'culvert' as
86397 // the _oldType to default to if the user picks 'tunnel'
86398 if (!!tags.waterway && !_oldType.tunnel) {
86399 _oldType.tunnel = 'culvert';
86402 wrap.call(structureExtras, tags);
86406 radio.focus = function () {
86407 radios.node().focus();
86410 radio.entityIDs = function (val) {
86411 if (!arguments.length) return _entityIDs;
86417 radio.isAllowed = function () {
86418 return _entityIDs.length === 1;
86421 return utilRebind(radio, dispatch$1, 'on');
86424 function uiFieldRestrictions(field, context) {
86425 var dispatch$1 = dispatch('change');
86426 var breathe = behaviorBreathe();
86427 corePreferences('turn-restriction-via-way', null); // remove old key
86429 var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922
86431 var storedDistance = corePreferences('turn-restriction-distance');
86433 var _maxViaWay = storedViaWay !== null ? +storedViaWay : 0;
86435 var _maxDistance = storedDistance ? +storedDistance : 30;
86437 var _initialized = false;
86439 var _parent = select(null); // the entire field
86442 var _container = select(null); // just the map
86457 function restrictions(selection) {
86458 _parent = selection; // try to reuse the intersection, but always rebuild it if the graph has changed
86460 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86461 _graph = context.graph();
86462 _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86463 } // It's possible for there to be no actual intersection here.
86464 // for example, a vertex of two `highway=path`
86465 // In this case, hide the field.
86468 var isOK = _intersection && _intersection.vertices.length && // has vertices
86469 _intersection.vertices // has the vertex that the user selected
86470 .filter(function (vertex) {
86471 return vertex.id === _vertexID;
86472 }).length && _intersection.ways.length > 2 && // has more than 2 ways
86473 _intersection.ways // has more than 1 TO way
86474 .filter(function (way) {
86476 }).length > 1; // Also hide in the case where
86478 select(selection.node().parentNode).classed('hide', !isOK); // if form field is hidden or has detached from dom, clean up.
86480 if (!isOK || !context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode || !selection.node().parentNode.parentNode) {
86481 selection.call(restrictions.off);
86485 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86486 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86487 var container = wrap.selectAll('.restriction-container').data([0]); // enter
86489 var containerEnter = container.enter().append('div').attr('class', 'restriction-container');
86490 containerEnter.append('div').attr('class', 'restriction-help'); // update
86492 _container = containerEnter.merge(container).call(renderViewer);
86493 var controls = wrap.selectAll('.restriction-controls').data([0]); // enter/update
86495 controls.enter().append('div').attr('class', 'restriction-controls-container').append('div').attr('class', 'restriction-controls').merge(controls).call(renderControls);
86498 function renderControls(selection) {
86499 var distControl = selection.selectAll('.restriction-distance').data([0]);
86500 distControl.exit().remove();
86501 var distControlEnter = distControl.enter().append('div').attr('class', 'restriction-control restriction-distance');
86502 distControlEnter.append('span').attr('class', 'restriction-control-label restriction-distance-label').html(_t.html('restriction.controls.distance') + ':');
86503 distControlEnter.append('input').attr('class', 'restriction-distance-input').attr('type', 'range').attr('min', '20').attr('max', '50').attr('step', '5');
86504 distControlEnter.append('span').attr('class', 'restriction-distance-text'); // update
86506 selection.selectAll('.restriction-distance-input').property('value', _maxDistance).on('input', function () {
86507 var val = select(this).property('value');
86508 _maxDistance = +val;
86509 _intersection = null;
86511 _container.selectAll('.layer-osm .layer-turns *').remove();
86513 corePreferences('turn-restriction-distance', _maxDistance);
86515 _parent.call(restrictions);
86517 selection.selectAll('.restriction-distance-text').html(displayMaxDistance(_maxDistance));
86518 var viaControl = selection.selectAll('.restriction-via-way').data([0]);
86519 viaControl.exit().remove();
86520 var viaControlEnter = viaControl.enter().append('div').attr('class', 'restriction-control restriction-via-way');
86521 viaControlEnter.append('span').attr('class', 'restriction-control-label restriction-via-way-label').html(_t.html('restriction.controls.via') + ':');
86522 viaControlEnter.append('input').attr('class', 'restriction-via-way-input').attr('type', 'range').attr('min', '0').attr('max', '2').attr('step', '1');
86523 viaControlEnter.append('span').attr('class', 'restriction-via-way-text'); // update
86525 selection.selectAll('.restriction-via-way-input').property('value', _maxViaWay).on('input', function () {
86526 var val = select(this).property('value');
86529 _container.selectAll('.layer-osm .layer-turns *').remove();
86531 corePreferences('turn-restriction-via-way0', _maxViaWay);
86533 _parent.call(restrictions);
86535 selection.selectAll('.restriction-via-way-text').html(displayMaxVia(_maxViaWay));
86538 function renderViewer(selection) {
86539 if (!_intersection) return;
86540 var vgraph = _intersection.graph;
86541 var filter = utilFunctor(true);
86542 var projection = geoRawMercator(); // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86543 // Instead of asking the restriction-container for its dimensions,
86544 // we can ask the .sidebar, which can have its dimensions cached.
86545 // width: calc as sidebar - padding
86546 // height: hardcoded (from `80_app.css`)
86547 // var d = utilGetDimensions(selection);
86549 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86550 var d = [sdims[0] - 50, 370];
86551 var c = geoVecScale(d, 0.5);
86553 projection.scale(geoZoomToScale(z)); // Calculate extent of all key vertices
86555 var extent = geoExtent();
86557 for (var i = 0; i < _intersection.vertices.length; i++) {
86558 extent._extend(_intersection.vertices[i].extent());
86559 } // If this is a large intersection, adjust zoom to fit extent
86562 if (_intersection.vertices.length > 1) {
86563 var padding = 180; // in z22 pixels
86565 var tl = projection([extent[0][0], extent[1][1]]);
86566 var br = projection([extent[1][0], extent[0][1]]);
86567 var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86568 var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86569 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86570 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86571 z = z - Math.max(hZoomDiff, vZoomDiff);
86572 projection.scale(geoZoomToScale(z));
86575 var padTop = 35; // reserve top space for hint text
86577 var extentCenter = projection(extent.center());
86578 extentCenter[1] = extentCenter[1] - padTop;
86579 projection.translate(geoVecSubtract(c, extentCenter)).clipExtent([[0, 0], d]);
86580 var drawLayers = svgLayers(projection, context).only(['osm', 'touch']).dimensions(d);
86581 var drawVertices = svgVertices(projection, context);
86582 var drawLines = svgLines(projection, context);
86583 var drawTurns = svgTurns(projection, context);
86584 var firstTime = selection.selectAll('.surface').empty();
86585 selection.call(drawLayers);
86586 var surface = selection.selectAll('.surface').classed('tr', true);
86589 _initialized = true;
86590 surface.call(breathe);
86591 } // This can happen if we've lowered the detail while a FROM way
86592 // is selected, and that way is no longer part of the intersection.
86595 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86600 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));
86601 surface.on('click.restrictions', click).on('mouseover.restrictions', mouseover);
86602 surface.selectAll('.selected').classed('selected', false);
86603 surface.selectAll('.related').classed('related', false);
86607 way = vgraph.entity(_fromWayID);
86608 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
86611 document.addEventListener('resizeWindow', function () {
86612 utilSetDimensions(_container, null);
86617 function click(d3_event) {
86618 surface.call(breathe.off).call(breathe);
86619 var datum = d3_event.target.__data__;
86620 var entity = datum && datum.properties && datum.properties.entity;
86626 if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86627 _fromWayID = datum.id;
86630 } else if (datum instanceof osmTurn) {
86631 var actions, extraActions, turns, i;
86632 var restrictionType = osmInferRestriction(vgraph, datum, projection);
86634 if (datum.restrictionID && !datum.direct) {
86636 } else if (datum.restrictionID && !datum.only) {
86639 var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum
86641 datumOnly.only = true; // but change this property
86643 restrictionType = restrictionType.replace(/^no/, 'only'); // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86644 // We will remember them in _oldTurns, and restore them if the user clicks again.
86646 turns = _intersection.turns(_fromWayID, 2);
86650 for (i = 0; i < turns.length; i++) {
86651 var turn = turns[i];
86652 if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928)
86654 if (turn.direct && turn.path[1] === datum.path[1]) {
86655 seen[turns[i].restrictionID] = true;
86656 turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86658 _oldTurns.push(turn);
86660 extraActions.push(actionUnrestrictTurn(turn));
86664 actions = _intersection.actions.concat(extraActions, [actionRestrictTurn(datumOnly, restrictionType), _t('operations.restriction.annotation.create')]);
86665 } else if (datum.restrictionID) {
86667 // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86668 // This relies on the assumption that the intersection was already split up when we
86669 // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86670 turns = _oldTurns || [];
86673 for (i = 0; i < turns.length; i++) {
86674 if (turns[i].key !== datum.key) {
86675 extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86680 actions = _intersection.actions.concat(extraActions, [actionUnrestrictTurn(datum), _t('operations.restriction.annotation.delete')]);
86683 actions = _intersection.actions.concat([actionRestrictTurn(datum, restrictionType), _t('operations.restriction.annotation.create')]);
86686 context.perform.apply(context, actions); // At this point the datum will be changed, but will have same key..
86687 // Refresh it and update the help..
86689 var s = surface.selectAll('.' + datum.key);
86690 datum = s.empty() ? null : s.datum();
86691 updateHints(datum);
86699 function mouseover(d3_event) {
86700 var datum = d3_event.target.__data__;
86701 updateHints(datum);
86704 _lastXPos = _lastXPos || sdims[0];
86706 function redraw(minChange) {
86710 xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
86713 if (!minChange || minChange && Math.abs(xPos - _lastXPos) >= minChange) {
86714 if (context.hasEntity(_vertexID)) {
86717 _container.call(renderViewer);
86722 function highlightPathsFrom(wayID) {
86723 surface.selectAll('.related').classed('related', false).classed('allow', false).classed('restrict', false).classed('only', false);
86724 surface.selectAll('.' + wayID).classed('related', true);
86727 var turns = _intersection.turns(wayID, _maxViaWay);
86729 for (var i = 0; i < turns.length; i++) {
86730 var turn = turns[i];
86731 var ids = [turn.to.way];
86732 var klass = turn.no ? 'restrict' : turn.only ? 'only' : 'allow';
86734 if (turn.only || turns.length === 1) {
86735 if (turn.via.ways) {
86736 ids = ids.concat(turn.via.ways);
86738 } else if (turn.to.way === wayID) {
86742 surface.selectAll(utilEntitySelector(ids)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only');
86747 function updateHints(datum) {
86748 var help = _container.selectAll('.restriction-help').html('');
86750 var placeholders = {};
86751 ['from', 'via', 'to'].forEach(function (k) {
86752 placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
86754 var entity = datum && datum.properties && datum.properties.entity;
86761 way = vgraph.entity(_fromWayID);
86762 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
86763 } // Hovering a way
86766 if (datum instanceof osmWay && datum.__from) {
86768 highlightPathsFrom(_fromWayID ? null : way.id);
86769 surface.selectAll('.' + way.id).classed('related', true);
86770 var clickSelect = !_fromWayID || _fromWayID !== way.id;
86771 help.append('div') // "Click to select FROM {fromName}." / "FROM {fromName}"
86772 .html(_t.html('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
86773 from: placeholders.from,
86774 fromName: displayName(way.id, vgraph)
86775 })); // Hovering a turn arrow
86776 } else if (datum instanceof osmTurn) {
86777 var restrictionType = osmInferRestriction(vgraph, datum, projection);
86778 var turnType = restrictionType.replace(/^(only|no)\_/, '');
86779 var indirect = datum.direct === false ? _t.html('restriction.help.indirect') : '';
86780 var klass, turnText, nextText;
86783 klass = 'restrict';
86784 turnText = _t.html('restriction.help.turn.no_' + turnType, {
86787 nextText = _t.html('restriction.help.turn.only_' + turnType, {
86790 } else if (datum.only) {
86792 turnText = _t.html('restriction.help.turn.only_' + turnType, {
86795 nextText = _t.html('restriction.help.turn.allowed_' + turnType, {
86800 turnText = _t.html('restriction.help.turn.allowed_' + turnType, {
86803 nextText = _t.html('restriction.help.turn.no_' + turnType, {
86808 help.append('div') // "NO Right Turn (indirect)"
86809 .attr('class', 'qualifier ' + klass).html(turnText);
86810 help.append('div') // "FROM {fromName} TO {toName}"
86811 .html(_t.html('restriction.help.from_name_to_name', {
86812 from: placeholders.from,
86813 fromName: displayName(datum.from.way, vgraph),
86814 to: placeholders.to,
86815 toName: displayName(datum.to.way, vgraph)
86818 if (datum.via.ways && datum.via.ways.length) {
86821 for (var i = 0; i < datum.via.ways.length; i++) {
86822 var prev = names[names.length - 1];
86823 var curr = displayName(datum.via.ways[i], vgraph);
86824 if (!prev || curr !== prev) // collapse identical names
86828 help.append('div') // "VIA {viaNames}"
86829 .html(_t.html('restriction.help.via_names', {
86830 via: placeholders.via,
86831 viaNames: names.join(', ')
86836 help.append('div') // Click for "No Right Turn"
86837 .html(_t.html('restriction.help.toggle', {
86838 turn: nextText.trim()
86842 highlightPathsFrom(null);
86843 var alongIDs = datum.path.slice();
86844 surface.selectAll(utilEntitySelector(alongIDs)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); // Hovering empty surface
86846 highlightPathsFrom(null);
86849 help.append('div') // "FROM {fromName}"
86850 .html(_t.html('restriction.help.from_name', {
86851 from: placeholders.from,
86852 fromName: displayName(_fromWayID, vgraph)
86855 help.append('div') // "Click to select a FROM segment."
86856 .html(_t.html('restriction.help.select_from', {
86857 from: placeholders.from
86864 function displayMaxDistance(maxDist) {
86865 var isImperial = !_mainLocalizer.usesMetric();
86870 // imprecise conversion for prettier display
86880 distance: _t('units.feet', {
86881 quantity: distToFeet
86886 distance: _t('units.meters', {
86892 return _t.html('restriction.controls.distance_up_to', opts);
86895 function displayMaxVia(maxVia) {
86896 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');
86899 function displayName(entityID, graph) {
86900 var entity = graph.entity(entityID);
86901 var name = utilDisplayName(entity) || '';
86902 var matched = _mainPresetIndex.match(entity, graph);
86903 var type = matched && matched.name() || utilDisplayType(entity.id);
86904 return name || type;
86907 restrictions.entityIDs = function (val) {
86908 _intersection = null;
86911 _vertexID = val[0];
86914 restrictions.tags = function () {};
86916 restrictions.focus = function () {};
86918 restrictions.off = function (selection) {
86919 if (!_initialized) return;
86920 selection.selectAll('.surface').call(breathe.off).on('click.restrictions', null).on('mouseover.restrictions', null);
86921 select(window).on('resize.restrictions', null);
86924 return utilRebind(restrictions, dispatch$1, 'on');
86926 uiFieldRestrictions.supportsMultiselection = false;
86928 function uiFieldTextarea(field, context) {
86929 var dispatch$1 = dispatch('change');
86930 var input = select(null);
86934 function textarea(selection) {
86935 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86936 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86937 input = wrap.selectAll('textarea').data([0]);
86938 input = input.enter().append('textarea').attr('id', field.domId).call(utilNoAuto).on('input', change(true)).on('blur', change()).on('change', change()).merge(input);
86941 function change(onInput) {
86942 return function () {
86943 var val = utilGetSetValue(input);
86944 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
86946 if (!val && Array.isArray(_tags[field.key])) return;
86948 t[field.key] = val || undefined;
86949 dispatch$1.call('change', this, t, onInput);
86953 textarea.tags = function (tags) {
86955 var isMixed = Array.isArray(tags[field.key]);
86956 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);
86959 textarea.focus = function () {
86960 input.node().focus();
86963 return utilRebind(textarea, dispatch$1, 'on');
86966 function uiFieldWikidata(field, context) {
86967 var wikidata = services.wikidata;
86968 var dispatch$1 = dispatch('change');
86970 var _selection = select(null);
86972 var _searchInput = select(null);
86975 var _wikidataEntity = null;
86977 var _entityIDs = [];
86979 var _wikipediaKey = field.keys && field.keys.find(function (key) {
86980 return key.includes('wikipedia');
86982 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
86984 var combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(true).minItems(1);
86986 function wiki(selection) {
86987 _selection = selection;
86988 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86989 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86990 var list = wrap.selectAll('ul').data([0]);
86991 list = list.enter().append('ul').attr('class', 'rows').merge(list);
86992 var searchRow = list.selectAll('li.wikidata-search').data([0]);
86993 var searchRowEnter = searchRow.enter().append('li').attr('class', 'wikidata-search');
86994 searchRowEnter.append('input').attr('type', 'text').attr('id', field.domId).style('flex', '1').call(utilNoAuto).on('focus', function () {
86995 var node = select(this).node();
86996 node.setSelectionRange(0, node.value.length);
86997 }).on('blur', function () {
86998 setLabelForEntity();
86999 }).call(combobox.fetcher(fetchWikidataItems));
87000 combobox.on('accept', function (d) {
87005 }).on('cancel', function () {
87006 setLabelForEntity();
87008 searchRowEnter.append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
87009 domain: 'wikidata.org'
87010 })).call(svgIcon('#iD-icon-out-link')).on('click', function (d3_event) {
87011 d3_event.preventDefault();
87012 if (_wikiURL) window.open(_wikiURL, '_blank');
87014 searchRow = searchRow.merge(searchRowEnter);
87015 _searchInput = searchRow.select('input');
87016 var wikidataProperties = ['description', 'identifier'];
87017 var items = list.selectAll('li.labeled-input').data(wikidataProperties); // Enter
87019 var enter = items.enter().append('li').attr('class', function (d) {
87020 return 'labeled-input preset-wikidata-' + d;
87022 enter.append('span').attr('class', 'label').html(function (d) {
87023 return _t.html('wikidata.' + d);
87025 enter.append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', 'true').attr('readonly', 'true');
87026 enter.append('button').attr('class', 'form-field-button').attr('title', _t('icons.copy')).call(svgIcon('#iD-operation-copy')).on('click', function (d3_event) {
87027 d3_event.preventDefault();
87028 select(this.parentNode).select('input').node().select();
87029 document.execCommand('copy');
87033 function fetchWikidataItems(q, callback) {
87034 if (!q && _hintKey) {
87035 // other tags may be good search terms
87036 for (var i in _entityIDs) {
87037 var entity = context.hasEntity(_entityIDs[i]);
87039 if (entity.tags[_hintKey]) {
87040 q = entity.tags[_hintKey];
87046 wikidata.itemsForSearchQuery(q, function (err, data) {
87049 for (var i in data) {
87050 data[i].value = data[i].label + ' (' + data[i].id + ')';
87051 data[i].title = data[i].description;
87054 if (callback) callback(data);
87058 function change() {
87060 syncTags[field.key] = _qid;
87061 dispatch$1.call('change', this, syncTags); // attempt asynchronous update of wikidata tag..
87063 var initGraph = context.graph();
87064 var initEntityIDs = _entityIDs;
87065 wikidata.entityByQID(_qid, function (err, entity) {
87066 if (err) return; // If graph has changed, we can't apply this update.
87068 if (context.graph() !== initGraph) return;
87069 if (!entity.sitelinks) return;
87070 var langs = wikidata.languagesToQuery(); // use the label and description languages as fallbacks
87072 ['labels', 'descriptions'].forEach(function (key) {
87073 if (!entity[key]) return;
87074 var valueLangs = Object.keys(entity[key]);
87075 if (valueLangs.length === 0) return;
87076 var valueLang = valueLangs[0];
87078 if (langs.indexOf(valueLang) === -1) {
87079 langs.push(valueLang);
87082 var newWikipediaValue;
87084 if (_wikipediaKey) {
87085 var foundPreferred;
87087 for (var i in langs) {
87088 var lang = langs[i];
87089 var siteID = lang.replace('-', '_') + 'wiki';
87091 if (entity.sitelinks[siteID]) {
87092 foundPreferred = true;
87093 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; // use the first match
87099 if (!foundPreferred) {
87100 // No wikipedia sites available in the user's language or the fallback languages,
87101 // default to any wikipedia sitelink
87102 var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function (site) {
87103 return site.endsWith('wiki');
87106 if (wikiSiteKeys.length === 0) {
87107 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87108 newWikipediaValue = null;
87110 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87111 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87112 newWikipediaValue = wikiLang + ':' + wikiTitle;
87117 if (newWikipediaValue) {
87118 newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87121 if (typeof newWikipediaValue === 'undefined') return;
87122 var actions = initEntityIDs.map(function (entityID) {
87123 var entity = context.hasEntity(entityID);
87124 if (!entity) return null;
87125 var currTags = Object.assign({}, entity.tags); // shallow copy
87127 if (newWikipediaValue === null) {
87128 if (!currTags[_wikipediaKey]) return null;
87129 delete currTags[_wikipediaKey];
87131 currTags[_wikipediaKey] = newWikipediaValue;
87134 return actionChangeTags(entityID, currTags);
87135 }).filter(Boolean);
87136 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
87138 context.overwrite(function actionUpdateWikipediaTags(graph) {
87139 actions.forEach(function (action) {
87140 graph = action(graph);
87143 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
87144 // changeTags() is not intended to be called asynchronously
87148 function setLabelForEntity() {
87151 if (_wikidataEntity) {
87152 label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87154 if (label.length === 0) {
87155 label = _wikidataEntity.id.toString();
87159 utilGetSetValue(_searchInput, label);
87162 wiki.tags = function (tags) {
87163 var isMixed = Array.isArray(tags[field.key]);
87165 _searchInput.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : '').classed('mixed', isMixed);
87167 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87169 if (!/^Q[0-9]*$/.test(_qid)) {
87170 // not a proper QID
87173 } // QID value in correct format
87176 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87177 wikidata.entityByQID(_qid, function (err, entity) {
87183 _wikidataEntity = entity;
87184 setLabelForEntity();
87185 var description = entityPropertyForDisplay(entity, 'descriptions');
87187 _selection.select('button.wiki-link').classed('disabled', false);
87189 _selection.select('.preset-wikidata-description').style('display', function () {
87190 return description.length > 0 ? 'flex' : 'none';
87191 }).select('input').attr('value', description);
87193 _selection.select('.preset-wikidata-identifier').style('display', function () {
87194 return entity.id ? 'flex' : 'none';
87195 }).select('input').attr('value', entity.id);
87196 }); // not a proper QID
87198 function unrecognized() {
87199 _wikidataEntity = null;
87200 setLabelForEntity();
87202 _selection.select('.preset-wikidata-description').style('display', 'none');
87204 _selection.select('.preset-wikidata-identifier').style('display', 'none');
87206 _selection.select('button.wiki-link').classed('disabled', true);
87208 if (_qid && _qid !== '') {
87209 _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87216 function entityPropertyForDisplay(wikidataEntity, propKey) {
87217 if (!wikidataEntity[propKey]) return '';
87218 var propObj = wikidataEntity[propKey];
87219 var langKeys = Object.keys(propObj);
87220 if (langKeys.length === 0) return ''; // sorted by priority, since we want to show the user's language first if possible
87222 var langs = wikidata.languagesToQuery();
87224 for (var i in langs) {
87225 var lang = langs[i];
87226 var valueObj = propObj[lang];
87227 if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
87228 } // default to any available value
87231 return propObj[langKeys[0]].value;
87234 wiki.entityIDs = function (val) {
87235 if (!arguments.length) return _entityIDs;
87240 wiki.focus = function () {
87241 _searchInput.node().focus();
87244 return utilRebind(wiki, dispatch$1, 'on');
87247 function uiFieldWikipedia(field, context) {
87248 var _arguments = arguments;
87249 var dispatch$1 = dispatch('change');
87250 var wikipedia = services.wikipedia;
87251 var wikidata = services.wikidata;
87253 var _langInput = select(null);
87255 var _titleInput = select(null);
87263 var _dataWikipedia = [];
87264 _mainFileFetcher.get('wmf_sitematrix').then(function (d) {
87265 _dataWikipedia = d;
87266 if (_tags) updateForTags(_tags);
87267 })["catch"](function () {
87270 var langCombo = uiCombobox(context, 'wikipedia-lang').fetcher(function (value, callback) {
87271 var v = value.toLowerCase();
87272 callback(_dataWikipedia.filter(function (d) {
87273 return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
87274 }).map(function (d) {
87280 var titleCombo = uiCombobox(context, 'wikipedia-title').fetcher(function (value, callback) {
87284 for (var i in _entityIDs) {
87285 var entity = context.hasEntity(_entityIDs[i]);
87287 if (entity.tags.name) {
87288 value = entity.tags.name;
87294 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87295 searchfn(language()[2], value, function (query, data) {
87296 callback(data.map(function (d) {
87304 function wiki(selection) {
87305 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
87306 wrap = wrap.enter().append('div').attr('class', "form-field-input-wrap form-field-input-".concat(field.type)).merge(wrap);
87307 var langContainer = wrap.selectAll('.wiki-lang-container').data([0]);
87308 langContainer = langContainer.enter().append('div').attr('class', 'wiki-lang-container').merge(langContainer);
87309 _langInput = langContainer.selectAll('input.wiki-lang').data([0]);
87310 _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);
87312 _langInput.on('blur', changeLang).on('change', changeLang);
87314 var titleContainer = wrap.selectAll('.wiki-title-container').data([0]);
87315 titleContainer = titleContainer.enter().append('div').attr('class', 'wiki-title-container').merge(titleContainer);
87316 _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
87317 _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
87319 _titleInput.on('blur', blur).on('change', change);
87321 var link = titleContainer.selectAll('.wiki-link').data([0]);
87322 link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
87323 domain: 'wikipedia.org'
87324 })).call(svgIcon('#iD-icon-out-link')).merge(link);
87325 link.on('click', function (d3_event) {
87326 d3_event.preventDefault();
87327 if (_wikiURL) window.open(_wikiURL, '_blank');
87331 function defaultLanguageInfo(skipEnglishFallback) {
87332 var langCode = _mainLocalizer.languageCode().toLowerCase();
87334 for (var i in _dataWikipedia) {
87335 var d = _dataWikipedia[i]; // default to the language of iD's current locale
87337 if (d[2] === langCode) return d;
87338 } // fallback to English
87341 return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
87344 function language(skipEnglishFallback) {
87345 var value = utilGetSetValue(_langInput).toLowerCase();
87347 for (var i in _dataWikipedia) {
87348 var d = _dataWikipedia[i]; // return the language already set in the UI, if supported
87350 if (d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value) return d;
87351 } // fallback to English
87354 return defaultLanguageInfo(skipEnglishFallback);
87357 function changeLang() {
87358 utilGetSetValue(_langInput, language()[1]);
87366 function change(skipWikidata) {
87367 var value = utilGetSetValue(_titleInput);
87368 var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87370 var langInfo = m && _dataWikipedia.find(function (d) {
87371 return m[1] === d[2];
87377 var nativeLangName = langInfo[1]; // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87379 value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87382 var anchor; // try {
87383 // leave this out for now - #6232
87384 // Best-effort `anchordecode:` implementation
87385 // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87388 anchor = decodeURIComponent(m[3]); // }
87390 value += '#' + anchor.replace(/_/g, ' ');
87393 value = value.slice(0, 1).toUpperCase() + value.slice(1);
87394 utilGetSetValue(_langInput, nativeLangName);
87395 utilGetSetValue(_titleInput, value);
87399 syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87401 syncTags.wikipedia = undefined;
87404 dispatch$1.call('change', this, syncTags);
87405 if (skipWikidata || !value || !language()[2]) return; // attempt asynchronous update of wikidata tag..
87407 var initGraph = context.graph();
87408 var initEntityIDs = _entityIDs;
87409 wikidata.itemsByTitle(language()[2], value, function (err, data) {
87410 if (err || !data || !Object.keys(data).length) return; // If graph has changed, we can't apply this update.
87412 if (context.graph() !== initGraph) return;
87413 var qids = Object.keys(data);
87414 var value = qids && qids.find(function (id) {
87415 return id.match(/^Q\d+$/);
87417 var actions = initEntityIDs.map(function (entityID) {
87418 var entity = context.entity(entityID).tags;
87419 var currTags = Object.assign({}, entity); // shallow copy
87421 if (currTags.wikidata !== value) {
87422 currTags.wikidata = value;
87423 return actionChangeTags(entityID, currTags);
87427 }).filter(Boolean);
87428 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
87430 context.overwrite(function actionUpdateWikidataTags(graph) {
87431 actions.forEach(function (action) {
87432 graph = action(graph);
87435 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
87436 // changeTags() is not intended to be called asynchronously
87440 wiki.tags = function (tags) {
87442 updateForTags(tags);
87445 function updateForTags(tags) {
87446 var value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
87447 // optional suffix of `#anchor`
87449 var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87450 var tagLang = m && m[1];
87451 var tagArticleTitle = m && m[2];
87452 var anchor = m && m[3];
87454 var tagLangInfo = tagLang && _dataWikipedia.find(function (d) {
87455 return tagLang === d[2];
87456 }); // value in correct format
87460 var nativeLangName = tagLangInfo[1];
87461 utilGetSetValue(_langInput, nativeLangName);
87462 utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? '#' + anchor : ''));
87466 // Best-effort `anchorencode:` implementation
87467 anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87469 anchor = anchor.replace(/ /g, '_');
87473 _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' + tagArticleTitle.replace(/ /g, '_') + (anchor ? '#' + anchor : ''); // unrecognized value format
87475 utilGetSetValue(_titleInput, value);
87477 if (value && value !== '') {
87478 utilGetSetValue(_langInput, '');
87479 var defaultLangInfo = defaultLanguageInfo();
87480 _wikiURL = "https://".concat(defaultLangInfo[2], ".wikipedia.org/w/index.php?fulltext=1&search=").concat(value);
87482 var shownOrDefaultLangInfo = language(true
87483 /* skipEnglishFallback */
87485 utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
87491 wiki.entityIDs = function (val) {
87492 if (!_arguments.length) return _entityIDs;
87497 wiki.focus = function () {
87498 _titleInput.node().focus();
87501 return utilRebind(wiki, dispatch$1, 'on');
87503 uiFieldWikipedia.supportsMultiselection = false;
87506 access: uiFieldAccess,
87507 address: uiFieldAddress,
87508 check: uiFieldCheck,
87509 combo: uiFieldCombo,
87510 cycleway: uiFieldCycleway,
87511 defaultCheck: uiFieldCheck,
87512 email: uiFieldText,
87513 identifier: uiFieldText,
87514 lanes: uiFieldLanes,
87515 localized: uiFieldLocalized,
87516 maxspeed: uiFieldMaxspeed,
87517 manyCombo: uiFieldCombo,
87518 multiCombo: uiFieldCombo,
87519 networkCombo: uiFieldCombo,
87520 number: uiFieldText,
87521 onewayCheck: uiFieldCheck,
87522 radio: uiFieldRadio,
87523 restrictions: uiFieldRestrictions,
87524 semiCombo: uiFieldCombo,
87525 structureRadio: uiFieldRadio,
87528 textarea: uiFieldTextarea,
87529 typeCombo: uiFieldCombo,
87531 wikidata: uiFieldWikidata,
87532 wikipedia: uiFieldWikipedia
87535 function uiField(context, presetField, entityIDs, options) {
87536 options = Object.assign({
87543 var dispatch$1 = dispatch('change', 'revert');
87544 var field = Object.assign({}, presetField); // shallow copy
87546 field.domId = utilUniqueDomId('form-field-' + field.safeid);
87547 var _show = options.show;
87550 var _locked = false;
87552 var _lockedTip = uiTooltip().title(_t.html('inspector.lock.suggestion', {
87554 })).placement('bottom');
87556 field.keys = field.keys || [field.key]; // only create the fields that are actually being shown
87558 if (_show && !field.impl) {
87560 } // Creates the field.. This is done lazily,
87561 // once we know that the field will be shown.
87564 function createField() {
87565 field.impl = uiFields[field.type](field, context).on('change', function (t, onInput) {
87566 dispatch$1.call('change', field, t, onInput);
87570 field.entityIDs = entityIDs; // if this field cares about the entities, pass them along
87572 if (field.impl.entityIDs) {
87573 field.impl.entityIDs(entityIDs);
87578 function isModified() {
87579 if (!entityIDs || !entityIDs.length) return false;
87580 return entityIDs.some(function (entityID) {
87581 var original = context.graph().base().entities[entityID];
87582 var latest = context.graph().entity(entityID);
87583 return field.keys.some(function (key) {
87584 return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
87589 function tagsContainFieldKey() {
87590 return field.keys.some(function (key) {
87591 if (field.type === 'multiCombo') {
87592 for (var tagKey in _tags) {
87593 if (tagKey.indexOf(key) === 0) {
87601 return _tags[key] !== undefined;
87605 function revert(d3_event, d) {
87606 d3_event.stopPropagation();
87607 d3_event.preventDefault();
87608 if (!entityIDs || _locked) return;
87609 dispatch$1.call('revert', d, d.keys);
87612 function remove(d3_event, d) {
87613 d3_event.stopPropagation();
87614 d3_event.preventDefault();
87615 if (_locked) return;
87617 d.keys.forEach(function (key) {
87618 t[key] = undefined;
87620 dispatch$1.call('change', d, t);
87623 field.render = function (selection) {
87624 var container = selection.selectAll('.form-field').data([field]); // Enter
87626 var enter = container.enter().append('div').attr('class', function (d) {
87627 return 'form-field form-field-' + d.safeid;
87628 }).classed('nowrap', !options.wrap);
87630 if (options.wrap) {
87631 var labelEnter = enter.append('label').attr('class', 'field-label').attr('for', function (d) {
87634 var textEnter = labelEnter.append('span').attr('class', 'label-text');
87635 textEnter.append('span').attr('class', 'label-textvalue').html(function (d) {
87638 textEnter.append('span').attr('class', 'label-textannotation');
87640 if (options.remove) {
87641 labelEnter.append('button').attr('class', 'remove-icon').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete'));
87644 if (options.revert) {
87645 labelEnter.append('button').attr('class', 'modified-icon').attr('title', _t('icons.undo')).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo'));
87650 container = container.merge(enter);
87651 container.select('.field-label > .remove-icon') // propagate bound data
87652 .on('click', remove);
87653 container.select('.field-label > .modified-icon') // propagate bound data
87654 .on('click', revert);
87655 container.each(function (d) {
87656 var selection = select(this);
87662 var reference, help; // instantiate field help
87664 if (options.wrap && field.type === 'restrictions') {
87665 help = uiFieldHelp(context, 'restrictions');
87666 } // instantiate tag reference
87669 if (options.wrap && options.info) {
87670 var referenceKey = d.key || '';
87672 if (d.type === 'multiCombo') {
87673 // lookup key without the trailing ':'
87674 referenceKey = referenceKey.replace(/:$/, '');
87677 reference = uiTagReference(d.reference || {
87681 if (_state === 'hover') {
87682 reference.showing(false);
87686 selection.call(d.impl); // add field help components
87689 selection.call(help.body).select('.field-label').call(help.button);
87690 } // add tag reference components
87694 selection.call(reference.body).select('.field-label').call(reference.button);
87697 d.impl.tags(_tags);
87699 container.classed('locked', _locked).classed('modified', isModified()).classed('present', tagsContainFieldKey()); // show a tip and lock icon if the field is locked
87701 var annotation = container.selectAll('.field-label .label-textannotation');
87702 var icon = annotation.selectAll('.icon').data(_locked ? [0] : []);
87703 icon.exit().remove();
87704 icon.enter().append('svg').attr('class', 'icon').append('use').attr('xlink:href', '#fas-lock');
87705 container.call(_locked ? _lockedTip : _lockedTip.destroy);
87708 field.state = function (val) {
87709 if (!arguments.length) return _state;
87714 field.tags = function (val) {
87715 if (!arguments.length) return _tags;
87718 if (tagsContainFieldKey() && !_show) {
87719 // always show a field if it has a value to display
87730 field.locked = function (val) {
87731 if (!arguments.length) return _locked;
87736 field.show = function () {
87743 if (field["default"] && field.key && _tags[field.key] !== field["default"]) {
87745 t[field.key] = field["default"];
87746 dispatch$1.call('change', this, t);
87748 }; // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
87751 field.isShown = function () {
87753 }; // An allowed field can appear in the UI or in the 'Add field' dropdown.
87754 // A non-allowed field is hidden from the user altogether
87757 field.isAllowed = function () {
87758 if (entityIDs && entityIDs.length > 1 && uiFields[field.type].supportsMultiselection === false) return false;
87759 if (field.geometry && !entityIDs.every(function (entityID) {
87760 return field.matchGeometry(context.graph().geometry(entityID));
87763 if (field.countryCodes || field.notCountryCodes) {
87764 var extent = combinedEntityExtent();
87765 if (!extent) return true;
87766 var center = extent.center();
87767 var countryCode = iso1A2Code(center);
87768 if (!countryCode) return false;
87769 countryCode = countryCode.toLowerCase();
87771 if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
87775 if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
87780 var prerequisiteTag = field.prerequisiteTag;
87782 if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
87784 if (!entityIDs.every(function (entityID) {
87785 var entity = context.graph().entity(entityID);
87787 if (prerequisiteTag.key) {
87788 var value = entity.tags[prerequisiteTag.key];
87789 if (!value) return false;
87791 if (prerequisiteTag.valueNot) {
87792 return prerequisiteTag.valueNot !== value;
87795 if (prerequisiteTag.value) {
87796 return prerequisiteTag.value === value;
87798 } else if (prerequisiteTag.keyNot) {
87799 if (entity.tags[prerequisiteTag.keyNot]) return false;
87809 field.focus = function () {
87811 field.impl.focus();
87815 function combinedEntityExtent() {
87816 return entityIDs && entityIDs.length && entityIDs.reduce(function (extent, entityID) {
87817 var entity = context.graph().entity(entityID);
87818 return extent.extend(entity.extent(context.graph()));
87822 return utilRebind(field, dispatch$1, 'on');
87825 function uiFormFields(context) {
87826 var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
87827 var _fieldsArr = [];
87828 var _lastPlaceholder = '';
87832 function formFields(selection) {
87833 var allowedFields = _fieldsArr.filter(function (field) {
87834 return field.isAllowed();
87837 var shown = allowedFields.filter(function (field) {
87838 return field.isShown();
87840 var notShown = allowedFields.filter(function (field) {
87841 return !field.isShown();
87843 var container = selection.selectAll('.form-fields-container').data([0]);
87844 container = container.enter().append('div').attr('class', 'form-fields-container ' + (_klass || '')).merge(container);
87845 var fields = container.selectAll('.wrap-form-field').data(shown, function (d) {
87846 return d.id + (d.entityIDs ? d.entityIDs.join() : '');
87848 fields.exit().remove(); // Enter
87850 var enter = fields.enter().append('div').attr('class', function (d) {
87851 return 'wrap-form-field wrap-form-field-' + d.safeid;
87854 fields = fields.merge(enter);
87855 fields.order().each(function (d) {
87856 select(this).call(d.render);
87859 var moreFields = notShown.map(function (field) {
87860 var title = field.title();
87861 titles.push(title);
87862 var terms = field.terms();
87863 if (field.key) terms.push(field.key);
87864 if (field.keys) terms = terms.concat(field.keys);
87866 display: field.label(),
87873 var placeholder = titles.slice(0, 3).join(', ') + (titles.length > 3 ? '…' : '');
87874 var more = selection.selectAll('.more-fields').data(_state === 'hover' || moreFields.length === 0 ? [] : [0]);
87875 more.exit().remove();
87876 var moreEnter = more.enter().append('div').attr('class', 'more-fields').append('label');
87877 moreEnter.append('span').html(_t.html('inspector.add_fields'));
87878 more = moreEnter.merge(more);
87879 var input = more.selectAll('.value').data([0]);
87880 input.exit().remove();
87881 input = input.enter().append('input').attr('class', 'value').attr('type', 'text').attr('placeholder', placeholder).call(utilNoAuto).merge(input);
87882 input.call(utilGetSetValue, '').call(moreCombo.data(moreFields).on('accept', function (d) {
87883 if (!d) return; // user entered something that was not matched
87885 var field = d.field;
87887 selection.call(formFields); // rerender
87890 })); // avoid updating placeholder excessively (triggers style recalc)
87892 if (_lastPlaceholder !== placeholder) {
87893 input.attr('placeholder', placeholder);
87894 _lastPlaceholder = placeholder;
87898 formFields.fieldsArr = function (val) {
87899 if (!arguments.length) return _fieldsArr;
87900 _fieldsArr = val || [];
87904 formFields.state = function (val) {
87905 if (!arguments.length) return _state;
87910 formFields.klass = function (val) {
87911 if (!arguments.length) return _klass;
87919 function uiSectionPresetFields(context) {
87920 var section = uiSection('preset-fields', context).label(_t.html('inspector.fields')).disclosureContent(renderDisclosureContent);
87921 var dispatch$1 = dispatch('change', 'revert');
87922 var formFields = uiFormFields(context);
87934 function renderDisclosureContent(selection) {
87936 var graph = context.graph();
87937 var geometries = Object.keys(_entityIDs.reduce(function (geoms, entityID) {
87938 geoms[graph.entity(entityID).geometry(graph)] = true;
87941 var presetsManager = _mainPresetIndex;
87942 var allFields = [];
87943 var allMoreFields = [];
87944 var sharedTotalFields;
87946 _presets.forEach(function (preset) {
87947 var fields = preset.fields();
87948 var moreFields = preset.moreFields();
87949 allFields = utilArrayUnion(allFields, fields);
87950 allMoreFields = utilArrayUnion(allMoreFields, moreFields);
87952 if (!sharedTotalFields) {
87953 sharedTotalFields = utilArrayUnion(fields, moreFields);
87955 sharedTotalFields = sharedTotalFields.filter(function (field) {
87956 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
87961 var sharedFields = allFields.filter(function (field) {
87962 return sharedTotalFields.indexOf(field) !== -1;
87964 var sharedMoreFields = allMoreFields.filter(function (field) {
87965 return sharedTotalFields.indexOf(field) !== -1;
87968 sharedFields.forEach(function (field) {
87969 if (field.matchAllGeometry(geometries)) {
87970 _fieldsArr.push(uiField(context, field, _entityIDs));
87973 var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
87975 if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
87976 _fieldsArr.push(uiField(context, presetsManager.field('restrictions'), _entityIDs));
87979 var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
87980 additionalFields.sort(function (field1, field2) {
87981 return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
87983 additionalFields.forEach(function (field) {
87984 if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) {
87985 _fieldsArr.push(uiField(context, field, _entityIDs, {
87991 _fieldsArr.forEach(function (field) {
87992 field.on('change', function (t, onInput) {
87993 dispatch$1.call('change', field, _entityIDs, t, onInput);
87994 }).on('revert', function (keys) {
87995 dispatch$1.call('revert', field, keys);
88000 _fieldsArr.forEach(function (field) {
88001 field.state(_state).tags(_tags);
88004 selection.call(formFields.fieldsArr(_fieldsArr).state(_state).klass('grouped-items-area'));
88005 selection.selectAll('.wrap-form-field input').on('keydown', function (d3_event) {
88006 // if user presses enter, and combobox is not active, accept edits..
88007 if (d3_event.keyCode === 13 && // ↩ Return
88008 context.container().select('.combobox').empty()) {
88009 context.enter(modeBrowse(context));
88014 section.presets = function (val) {
88015 if (!arguments.length) return _presets;
88017 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88025 section.state = function (val) {
88026 if (!arguments.length) return _state;
88031 section.tags = function (val) {
88032 if (!arguments.length) return _tags;
88033 _tags = val; // Don't reset _fieldsArr here.
88038 section.entityIDs = function (val) {
88039 if (!arguments.length) return _entityIDs;
88041 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88049 return utilRebind(section, dispatch$1, 'on');
88052 function uiSectionRawMemberEditor(context) {
88053 var section = uiSection('raw-member-editor', context).shouldDisplay(function () {
88054 if (!_entityIDs || _entityIDs.length !== 1) return false;
88055 var entity = context.hasEntity(_entityIDs[0]);
88056 return entity && entity.type === 'relation';
88057 }).label(function () {
88058 var entity = context.hasEntity(_entityIDs[0]);
88059 if (!entity) return '';
88060 var gt = entity.members.length > _maxMembers ? '>' : '';
88061 var count = gt + entity.members.slice(0, _maxMembers).length;
88062 return _t('inspector.title_count', {
88063 title: _t.html('inspector.members'),
88066 }).disclosureContent(renderDisclosureContent);
88067 var taginfo = services.taginfo;
88071 var _maxMembers = 1000;
88073 function downloadMember(d3_event, d) {
88074 d3_event.preventDefault(); // display the loading indicator
88076 select(this.parentNode).classed('tag-reference-loading', true);
88077 context.loadEntity(d.id, function () {
88078 section.reRender();
88082 function zoomToMember(d3_event, d) {
88083 d3_event.preventDefault();
88084 var entity = context.entity(d.id);
88085 context.map().zoomToEase(entity); // highlight the feature in case it wasn't previously on-screen
88087 utilHighlightEntities([d.id], true, context);
88090 function selectMember(d3_event, d) {
88091 d3_event.preventDefault(); // remove the hover-highlight styling
88093 utilHighlightEntities([d.id], false, context);
88094 var entity = context.entity(d.id);
88095 var mapExtent = context.map().extent();
88097 if (!entity.intersects(mapExtent, context.graph())) {
88098 // zoom to the entity if its extent is not visible now
88099 context.map().zoomToEase(entity);
88102 context.enter(modeSelect(context, [d.id]));
88105 function changeRole(d3_event, d) {
88106 var oldRole = d.role;
88107 var newRole = context.cleanRelationRole(select(this).property('value'));
88109 if (oldRole !== newRole) {
88115 context.perform(actionChangeMember(d.relation.id, member, d.index), _t('operations.change_role.annotation', {
88118 context.validator().validate();
88122 function deleteMember(d3_event, d) {
88123 // remove the hover-highlight styling
88124 utilHighlightEntities([d.id], false, context);
88125 context.perform(actionDeleteMember(d.relation.id, d.index), _t('operations.delete_member.annotation', {
88129 if (!context.hasEntity(d.relation.id)) {
88130 // Removing the last member will also delete the relation.
88131 // If this happens we need to exit the selection mode
88132 context.enter(modeBrowse(context));
88134 // Changing the mode also runs `validate`, but otherwise we need to
88135 // rerun it manually
88136 context.validator().validate();
88140 function renderDisclosureContent(selection) {
88141 var entityID = _entityIDs[0];
88142 var memberships = [];
88143 var entity = context.entity(entityID);
88144 entity.members.slice(0, _maxMembers).forEach(function (member, index) {
88151 member: context.hasEntity(member.id),
88152 domId: utilUniqueDomId(entityID + '-member-' + index)
88155 var list = selection.selectAll('.member-list').data([0]);
88156 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
88157 var items = list.selectAll('li').data(memberships, function (d) {
88158 return osmEntity.key(d.relation) + ',' + d.index + ',' + (d.member ? osmEntity.key(d.member) : 'incomplete');
88160 items.exit().each(unbind).remove();
88161 var itemsEnter = items.enter().append('li').attr('class', 'member-row form-field').classed('member-incomplete', function (d) {
88164 itemsEnter.each(function (d) {
88165 var item = select(this);
88166 var label = item.append('label').attr('class', 'field-label').attr('for', d.domId);
88169 // highlight the member feature in the map while hovering on the list item
88170 item.on('mouseover', function () {
88171 utilHighlightEntities([d.id], true, context);
88172 }).on('mouseout', function () {
88173 utilHighlightEntities([d.id], false, context);
88175 var labelLink = label.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectMember);
88176 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
88177 var matched = _mainPresetIndex.match(d.member, context.graph());
88178 return matched && matched.name() || utilDisplayType(d.member.id);
88180 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
88181 return utilDisplayName(d.member);
88183 label.append('button').attr('title', _t('icons.remove')).attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete'));
88184 label.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToMember);
88186 var labelText = label.append('span').attr('class', 'label-text');
88187 labelText.append('span').attr('class', 'member-entity-type').html(_t.html('inspector.' + d.type, {
88190 labelText.append('span').attr('class', 'member-entity-name').html(_t.html('inspector.incomplete', {
88193 label.append('button').attr('class', 'member-download').attr('title', _t('icons.download')).call(svgIcon('#iD-icon-load')).on('click', downloadMember);
88196 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88197 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
88199 }).property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto);
88202 wrapEnter.each(bindTypeahead);
88206 items = items.merge(itemsEnter).order();
88207 items.select('input.member-role').property('value', function (d) {
88209 }).on('blur', changeRole).on('change', changeRole);
88210 items.select('button.member-delete').on('click', deleteMember);
88211 var dragOrigin, targetIndex;
88212 items.call(d3_drag().on('start', function (d3_event) {
88217 targetIndex = null;
88218 }).on('drag', function (d3_event) {
88219 var x = d3_event.x - dragOrigin.x,
88220 y = d3_event.y - dragOrigin.y;
88221 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
88222 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
88223 var index = items.nodes().indexOf(this);
88224 select(this).classed('dragging', true);
88225 targetIndex = null;
88226 selection.selectAll('li.member-row').style('transform', function (d2, index2) {
88227 var node = select(this).node();
88229 if (index === index2) {
88230 return 'translate(' + x + 'px, ' + y + 'px)';
88231 } else if (index2 > index && d3_event.y > node.offsetTop) {
88232 if (targetIndex === null || index2 > targetIndex) {
88233 targetIndex = index2;
88236 return 'translateY(-100%)';
88237 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
88238 if (targetIndex === null || index2 < targetIndex) {
88239 targetIndex = index2;
88242 return 'translateY(100%)';
88247 }).on('end', function (d3_event, d) {
88248 if (!select(this).classed('dragging')) return;
88249 var index = items.nodes().indexOf(this);
88250 select(this).classed('dragging', false);
88251 selection.selectAll('li.member-row').style('transform', null);
88253 if (targetIndex !== null) {
88254 // dragged to a new position, reorder
88255 context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t('operations.reorder_members.annotation'));
88256 context.validator().validate();
88260 function bindTypeahead(d) {
88261 var row = select(this);
88262 var role = row.selectAll('input.member-role');
88263 var origValue = role.property('value');
88265 function sort(value, data) {
88266 var sameletter = [];
88269 for (var i = 0; i < data.length; i++) {
88270 if (data[i].value.substring(0, value.length) === value) {
88271 sameletter.push(data[i]);
88273 other.push(data[i]);
88277 return sameletter.concat(other);
88280 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
88281 // The `geometry` param is used in the `taginfo.js` interface for
88282 // filtering results, as a key into the `tag_members_fractions`
88283 // object. If we don't know the geometry because the member is
88284 // not yet downloaded, it's ok to guess based on type.
88288 geometry = context.graph().geometry(d.member.id);
88289 } else if (d.type === 'relation') {
88290 geometry = 'relation';
88291 } else if (d.type === 'way') {
88294 geometry = 'point';
88297 var rtype = entity.tags.type;
88300 rtype: rtype || '',
88301 geometry: geometry,
88303 }, function (err, data) {
88304 if (!err) callback(sort(role, data));
88306 }).on('cancel', function () {
88307 role.property('value', origValue);
88311 function unbind() {
88312 var row = select(this);
88313 row.selectAll('input.member-role').call(uiCombobox.off, context);
88317 section.entityIDs = function (val) {
88318 if (!arguments.length) return _entityIDs;
88326 function actionDeleteMembers(relationId, memberIndexes) {
88327 return function (graph) {
88328 // Remove the members in descending order so removals won't shift what members
88329 // are at the remaining indexes
88330 memberIndexes.sort(function (a, b) {
88334 for (var i in memberIndexes) {
88335 graph = actionDeleteMember(relationId, memberIndexes[i])(graph);
88342 function uiSectionRawMembershipEditor(context) {
88343 var section = uiSection('raw-membership-editor', context).shouldDisplay(function () {
88344 return _entityIDs && _entityIDs.length;
88345 }).label(function () {
88346 var parents = getSharedParentRelations();
88347 var gt = parents.length > _maxMemberships ? '>' : '';
88348 var count = gt + parents.slice(0, _maxMemberships).length;
88349 return _t('inspector.title_count', {
88350 title: _t.html('inspector.relations'),
88353 }).disclosureContent(renderDisclosureContent);
88354 var taginfo = services.taginfo;
88355 var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d) {
88356 if (d.relation) utilHighlightEntities([d.relation.id], true, context);
88357 }).itemsMouseLeave(function (d) {
88358 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
88360 var _inChange = false;
88361 var _entityIDs = [];
88365 var _maxMemberships = 1000;
88367 function getSharedParentRelations() {
88370 for (var i = 0; i < _entityIDs.length; i++) {
88371 var entity = context.graph().hasEntity(_entityIDs[i]);
88372 if (!entity) continue;
88375 parents = context.graph().parentRelations(entity);
88377 parents = utilArrayIntersection(parents, context.graph().parentRelations(entity));
88380 if (!parents.length) break;
88386 function getMemberships() {
88387 var memberships = [];
88388 var relations = getSharedParentRelations().slice(0, _maxMemberships);
88389 var isMultiselect = _entityIDs.length > 1;
88390 var i, relation, membership, index, member, indexedMember;
88392 for (i = 0; i < relations.length; i++) {
88393 relation = relations[i];
88395 relation: relation,
88397 hash: osmEntity.key(relation)
88400 for (index = 0; index < relation.members.length; index++) {
88401 member = relation.members[index];
88403 if (_entityIDs.indexOf(member.id) !== -1) {
88404 indexedMember = Object.assign({}, member, {
88407 membership.members.push(indexedMember);
88408 membership.hash += ',' + index.toString();
88410 if (!isMultiselect) {
88411 // For single selections, list one entry per membership per relation.
88412 // For multiselections, list one entry per relation.
88413 memberships.push(membership);
88415 relation: relation,
88417 hash: osmEntity.key(relation)
88423 if (membership.members.length) memberships.push(membership);
88426 memberships.forEach(function (membership) {
88427 membership.domId = utilUniqueDomId('membership-' + membership.relation.id);
88429 membership.members.forEach(function (member) {
88430 if (roles.indexOf(member.role) === -1) roles.push(member.role);
88432 membership.role = roles.length === 1 ? roles[0] : roles;
88434 return memberships;
88437 function selectRelation(d3_event, d) {
88438 d3_event.preventDefault(); // remove the hover-highlight styling
88440 utilHighlightEntities([d.relation.id], false, context);
88441 context.enter(modeSelect(context, [d.relation.id]));
88444 function zoomToRelation(d3_event, d) {
88445 d3_event.preventDefault();
88446 var entity = context.entity(d.relation.id);
88447 context.map().zoomToEase(entity); // highlight the relation in case it wasn't previously on-screen
88449 utilHighlightEntities([d.relation.id], true, context);
88452 function changeRole(d3_event, d) {
88453 if (d === 0) return; // called on newrow (shouldn't happen)
88455 if (_inChange) return; // avoid accidental recursive call #5731
88457 var newRole = context.cleanRelationRole(select(this).property('value'));
88458 if (!newRole.trim() && typeof d.role !== 'string') return;
88459 var membersToUpdate = d.members.filter(function (member) {
88460 return member.role !== newRole;
88463 if (membersToUpdate.length) {
88465 context.perform(function actionChangeMemberRoles(graph) {
88466 membersToUpdate.forEach(function (member) {
88467 var newMember = Object.assign({}, member, {
88470 delete newMember.index;
88471 graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
88474 }, _t('operations.change_role.annotation', {
88475 n: membersToUpdate.length
88477 context.validator().validate();
88483 function addMembership(d, role) {
88484 this.blur(); // avoid keeping focus on the button
88486 _showBlank = false;
88488 function actionAddMembers(relationId, ids, role) {
88489 return function (graph) {
88490 for (var i in ids) {
88493 type: graph.entity(ids[i]).type,
88496 graph = actionAddMember(relationId, member)(graph);
88504 context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t('operations.add_member.annotation', {
88505 n: _entityIDs.length
88507 context.validator().validate();
88509 var relation = osmRelation();
88510 context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t('operations.add.annotation.relation')); // changing the mode also runs `validate`
88512 context.enter(modeSelect(context, [relation.id]).newFeature(true));
88516 function deleteMembership(d3_event, d) {
88517 this.blur(); // avoid keeping focus on the button
88519 if (d === 0) return; // called on newrow (shouldn't happen)
88520 // remove the hover-highlight styling
88522 utilHighlightEntities([d.relation.id], false, context);
88523 var indexes = d.members.map(function (member) {
88524 return member.index;
88526 context.perform(actionDeleteMembers(d.relation.id, indexes), _t('operations.delete_member.annotation', {
88527 n: _entityIDs.length
88529 context.validator().validate();
88532 function fetchNearbyRelations(q, callback) {
88533 var newRelation = {
88535 value: _t('inspector.new_relation'),
88536 display: _t.html('inspector.new_relation')
88538 var entityID = _entityIDs[0];
88540 var graph = context.graph();
88542 function baseDisplayLabel(entity) {
88543 var matched = _mainPresetIndex.match(entity, graph);
88544 var presetName = matched && matched.name() || _t('inspector.relation');
88545 var entityName = utilDisplayName(entity) || '';
88546 return presetName + ' ' + entityName;
88549 var explicitRelation = q && context.hasEntity(q.toLowerCase());
88551 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
88552 // loaded relation is specified explicitly, only show that
88554 relation: explicitRelation,
88555 value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
88558 context.history().intersects(context.map().extent()).forEach(function (entity) {
88559 if (entity.type !== 'relation' || entity.id === entityID) return;
88560 var value = baseDisplayLabel(entity);
88561 if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
88567 result.sort(function (a, b) {
88568 return osmRelation.creationOrder(a.relation, b.relation);
88569 }); // Dedupe identical names by appending relation id - see #2891
88571 var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')).filter(function (v) {
88572 return v.length > 1;
88574 dupeGroups.forEach(function (group) {
88575 group.forEach(function (obj) {
88576 obj.value += ' ' + obj.relation.id;
88581 result.forEach(function (obj) {
88582 obj.title = obj.value;
88584 result.unshift(newRelation);
88588 function renderDisclosureContent(selection) {
88589 var memberships = getMemberships();
88590 var list = selection.selectAll('.member-list').data([0]);
88591 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
88592 var items = list.selectAll('li.member-row-normal').data(memberships, function (d) {
88595 items.exit().each(unbind).remove(); // Enter
88597 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
88599 itemsEnter.on('mouseover', function (d3_event, d) {
88600 utilHighlightEntities([d.relation.id], true, context);
88601 }).on('mouseout', function (d3_event, d) {
88602 utilHighlightEntities([d.relation.id], false, context);
88604 var labelEnter = itemsEnter.append('label').attr('class', 'field-label').attr('for', function (d) {
88607 var labelLink = labelEnter.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectRelation);
88608 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
88609 var matched = _mainPresetIndex.match(d.relation, context.graph());
88610 return matched && matched.name() || _t('inspector.relation');
88612 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
88613 return utilDisplayName(d.relation);
88615 labelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', deleteMembership);
88616 labelEnter.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToRelation);
88617 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88618 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
88620 }).property('type', 'text').property('value', function (d) {
88621 return typeof d.role === 'string' ? d.role : '';
88622 }).attr('title', function (d) {
88623 return Array.isArray(d.role) ? d.role.filter(Boolean).join('\n') : d.role;
88624 }).attr('placeholder', function (d) {
88625 return Array.isArray(d.role) ? _t('inspector.multiple_roles') : _t('inspector.role');
88626 }).classed('mixed', function (d) {
88627 return Array.isArray(d.role);
88628 }).call(utilNoAuto).on('blur', changeRole).on('change', changeRole);
88631 wrapEnter.each(bindTypeahead);
88634 var newMembership = list.selectAll('.member-row-new').data(_showBlank ? [0] : []); // Exit
88636 newMembership.exit().remove(); // Enter
88638 var newMembershipEnter = newMembership.enter().append('li').attr('class', 'member-row member-row-new form-field');
88639 var newLabelEnter = newMembershipEnter.append('label').attr('class', 'field-label');
88640 newLabelEnter.append('input').attr('placeholder', _t('inspector.choose_relation')).attr('type', 'text').attr('class', 'member-entity-input').call(utilNoAuto);
88641 newLabelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', function () {
88642 list.selectAll('.member-row-new').remove();
88644 var newWrapEnter = newMembershipEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88645 newWrapEnter.append('input').attr('class', 'member-role').property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); // Update
88647 newMembership = newMembership.merge(newMembershipEnter);
88648 newMembership.selectAll('.member-entity-input').on('blur', cancelEntity) // if it wasn't accepted normally, cancel it
88649 .call(nearbyCombo.on('accept', acceptEntity).on('cancel', cancelEntity)); // Container for the Add button
88651 var addRow = selection.selectAll('.add-row').data([0]); // enter
88653 var addRowEnter = addRow.enter().append('div').attr('class', 'add-row');
88654 var addRelationButton = addRowEnter.append('button').attr('class', 'add-relation');
88655 addRelationButton.call(svgIcon('#iD-icon-plus', 'light'));
88656 addRelationButton.call(uiTooltip().title(_t.html('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
88657 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
88659 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
88662 addRow = addRow.merge(addRowEnter);
88663 addRow.select('.add-relation').on('click', function () {
88665 section.reRender();
88666 list.selectAll('.member-entity-input').node().focus();
88669 function acceptEntity(d) {
88673 } // remove hover-higlighting
88676 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
88677 var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
88678 addMembership(d, role);
88681 function cancelEntity() {
88682 var input = newMembership.selectAll('.member-entity-input');
88683 input.property('value', ''); // remove hover-higlighting
88685 context.surface().selectAll('.highlighted').classed('highlighted', false);
88688 function bindTypeahead(d) {
88689 var row = select(this);
88690 var role = row.selectAll('input.member-role');
88691 var origValue = role.property('value');
88693 function sort(value, data) {
88694 var sameletter = [];
88697 for (var i = 0; i < data.length; i++) {
88698 if (data[i].value.substring(0, value.length) === value) {
88699 sameletter.push(data[i]);
88701 other.push(data[i]);
88705 return sameletter.concat(other);
88708 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
88709 var rtype = d.relation.tags.type;
88712 rtype: rtype || '',
88713 geometry: context.graph().geometry(_entityIDs[0]),
88715 }, function (err, data) {
88716 if (!err) callback(sort(role, data));
88718 }).on('cancel', function () {
88719 role.property('value', origValue);
88723 function unbind() {
88724 var row = select(this);
88725 row.selectAll('input.member-role').call(uiCombobox.off, context);
88729 section.entityIDs = function (val) {
88730 if (!arguments.length) return _entityIDs;
88732 _showBlank = false;
88739 function uiSectionSelectionList(context) {
88740 var _selectedIDs = [];
88741 var section = uiSection('selected-features', context).shouldDisplay(function () {
88742 return _selectedIDs.length > 1;
88743 }).label(function () {
88744 return _t('inspector.title_count', {
88745 title: _t.html('inspector.features'),
88746 count: _selectedIDs.length
88748 }).disclosureContent(renderDisclosureContent);
88749 context.history().on('change.selectionList', function (difference) {
88751 section.reRender();
88755 section.entityIDs = function (val) {
88756 if (!arguments.length) return _selectedIDs;
88757 _selectedIDs = val;
88761 function selectEntity(d3_event, entity) {
88762 context.enter(modeSelect(context, [entity.id]));
88765 function deselectEntity(d3_event, entity) {
88766 d3_event.stopPropagation();
88768 var selectedIDs = _selectedIDs.slice();
88770 var index = selectedIDs.indexOf(entity.id);
88773 selectedIDs.splice(index, 1);
88774 context.enter(modeSelect(context, selectedIDs));
88778 function renderDisclosureContent(selection) {
88779 var list = selection.selectAll('.feature-list').data([0]);
88780 list = list.enter().append('div').attr('class', 'feature-list').merge(list);
88782 var entities = _selectedIDs.map(function (id) {
88783 return context.hasEntity(id);
88784 }).filter(Boolean);
88786 var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key);
88787 items.exit().remove(); // Enter
88789 var enter = items.enter().append('button').attr('class', 'feature-list-item').on('click', selectEntity);
88790 enter.each(function (d) {
88791 select(this).on('mouseover', function () {
88792 utilHighlightEntities([d.id], true, context);
88794 select(this).on('mouseout', function () {
88795 utilHighlightEntities([d.id], false, context);
88798 var label = enter.append('div').attr('class', 'label');
88799 enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close'));
88800 label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text'));
88801 label.append('span').attr('class', 'entity-type');
88802 label.append('span').attr('class', 'entity-name'); // Update
88804 items = items.merge(enter);
88805 items.selectAll('.entity-geom-icon use').attr('href', function () {
88806 var entity = this.parentNode.parentNode.__data__;
88807 return '#iD-icon-' + entity.geometry(context.graph());
88809 items.selectAll('.entity-type').html(function (entity) {
88810 return _mainPresetIndex.match(entity, context.graph()).name();
88812 items.selectAll('.entity-name').html(function (d) {
88813 // fetch latest entity
88814 var entity = context.entity(d.id);
88815 return utilDisplayName(entity);
88822 function uiEntityEditor(context) {
88823 var dispatch$1 = dispatch('choose');
88824 var _state = 'select';
88825 var _coalesceChanges = false;
88826 var _modified = false;
88832 var _activePresets = [];
88838 function entityEditor(selection) {
88839 var combinedTags = utilCombinedTags(_entityIDs, context.graph()); // Header
88841 var header = selection.selectAll('.header').data([0]); // Enter
88843 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
88844 headerEnter.append('button').attr('class', 'preset-reset preset-choose').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-forward' : '#iD-icon-backward'));
88845 headerEnter.append('button').attr('class', 'close').on('click', function () {
88846 context.enter(modeBrowse(context));
88847 }).call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
88848 headerEnter.append('h3'); // Update
88850 header = header.merge(headerEnter);
88851 header.selectAll('h3').html(_entityIDs.length === 1 ? _t.html('inspector.edit') : _t.html('inspector.edit_features'));
88852 header.selectAll('.preset-reset').on('click', function () {
88853 dispatch$1.call('choose', this, _activePresets);
88856 var body = selection.selectAll('.inspector-body').data([0]); // Enter
88858 var bodyEnter = body.enter().append('div').attr('class', 'entity-editor inspector-body sep-top'); // Update
88860 body = body.merge(bodyEnter);
88863 _sections = [uiSectionSelectionList(context), uiSectionFeatureType(context).on('choose', function (presets) {
88864 dispatch$1.call('choose', this, presets);
88865 }), uiSectionEntityIssues(context), uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), uiSectionRawMemberEditor(context), uiSectionRawMembershipEditor(context)];
88868 _sections.forEach(function (section) {
88869 if (section.entityIDs) {
88870 section.entityIDs(_entityIDs);
88873 if (section.presets) {
88874 section.presets(_activePresets);
88877 if (section.tags) {
88878 section.tags(combinedTags);
88881 if (section.state) {
88882 section.state(_state);
88885 body.call(section.render);
88888 context.history().on('change.entity-editor', historyChanged);
88890 function historyChanged(difference) {
88891 if (selection.selectAll('.entity-editor').empty()) return;
88892 if (_state === 'hide') return;
88893 var significant = !difference || difference.didChange.properties || difference.didChange.addition || difference.didChange.deletion;
88894 if (!significant) return;
88895 _entityIDs = _entityIDs.filter(context.hasEntity);
88896 if (!_entityIDs.length) return;
88897 var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
88898 loadActivePresets();
88899 var graph = context.graph();
88900 entityEditor.modified(_base !== graph);
88901 entityEditor(selection);
88903 if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
88904 // flash the button to indicate the preset changed
88905 context.container().selectAll('.entity-editor button.preset-reset .label').style('background-color', '#fff').transition().duration(750).style('background-color', null);
88908 } // Tag changes that fire on input can all get coalesced into a single
88909 // history operation when the user leaves the field. #2342
88910 // Use explicit entityIDs in case the selection changes before the event is fired.
88913 function changeTags(entityIDs, changed, onInput) {
88916 for (var i in entityIDs) {
88917 var entityID = entityIDs[i];
88918 var entity = context.entity(entityID);
88919 var tags = Object.assign({}, entity.tags); // shallow copy
88921 for (var k in changed) {
88923 var v = changed[k];
88925 if (v !== undefined || tags.hasOwnProperty(k)) {
88931 tags = utilCleanTags(tags);
88934 if (!fastDeepEqual(entity.tags, tags)) {
88935 actions.push(actionChangeTags(entityID, tags));
88939 if (actions.length) {
88940 var combinedAction = function combinedAction(graph) {
88941 actions.forEach(function (action) {
88942 graph = action(graph);
88947 var annotation = _t('operations.change_tags.annotation');
88949 if (_coalesceChanges) {
88950 context.overwrite(combinedAction, annotation);
88952 context.perform(combinedAction, annotation);
88953 _coalesceChanges = !!onInput;
88955 } // if leaving field (blur event), rerun validation
88959 context.validator().validate();
88963 function revertTags(keys) {
88966 for (var i in _entityIDs) {
88967 var entityID = _entityIDs[i];
88968 var original = context.graph().base().entities[entityID];
88971 for (var j in keys) {
88973 changed[key] = original ? original.tags[key] : undefined;
88976 var entity = context.entity(entityID);
88977 var tags = Object.assign({}, entity.tags); // shallow copy
88979 for (var k in changed) {
88981 var v = changed[k];
88983 if (v !== undefined || tags.hasOwnProperty(k)) {
88988 tags = utilCleanTags(tags);
88990 if (!fastDeepEqual(entity.tags, tags)) {
88991 actions.push(actionChangeTags(entityID, tags));
88995 if (actions.length) {
88996 var combinedAction = function combinedAction(graph) {
88997 actions.forEach(function (action) {
88998 graph = action(graph);
89003 var annotation = _t('operations.change_tags.annotation');
89005 if (_coalesceChanges) {
89006 context.overwrite(combinedAction, annotation);
89008 context.perform(combinedAction, annotation);
89009 _coalesceChanges = false;
89013 context.validator().validate();
89016 entityEditor.modified = function (val) {
89017 if (!arguments.length) return _modified;
89019 return entityEditor;
89022 entityEditor.state = function (val) {
89023 if (!arguments.length) return _state;
89025 return entityEditor;
89028 entityEditor.entityIDs = function (val) {
89029 if (!arguments.length) return _entityIDs;
89030 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
89033 _base = context.graph();
89034 _coalesceChanges = false;
89035 loadActivePresets(true);
89036 return entityEditor.modified(false);
89039 entityEditor.newFeature = function (val) {
89040 if (!arguments.length) return _newFeature;
89042 return entityEditor;
89045 function loadActivePresets(isForNewSelection) {
89046 var graph = context.graph();
89049 for (var i in _entityIDs) {
89050 var entity = graph.hasEntity(_entityIDs[i]);
89051 if (!entity) return;
89052 var match = _mainPresetIndex.match(entity, graph);
89053 if (!counts[match.id]) counts[match.id] = 0;
89054 counts[match.id] += 1;
89057 var matches = Object.keys(counts).sort(function (p1, p2) {
89058 return counts[p2] - counts[p1];
89059 }).map(function (pID) {
89060 return _mainPresetIndex.item(pID);
89063 if (!isForNewSelection) {
89064 // A "weak" preset doesn't set any tags. (e.g. "Address")
89065 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")
89067 if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
89070 entityEditor.presets(matches);
89073 entityEditor.presets = function (val) {
89074 if (!arguments.length) return _activePresets; // don't reload the same preset
89076 if (!utilArrayIdentical(val, _activePresets)) {
89077 _activePresets = val;
89080 return entityEditor;
89083 return utilRebind(entityEditor, dispatch$1, 'on');
89086 function uiPresetList(context) {
89087 var dispatch$1 = dispatch('cancel', 'choose');
89091 var _currentPresets;
89093 var _autofocus = false;
89095 function presetList(selection) {
89096 if (!_entityIDs) return;
89097 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
89098 selection.html('');
89099 var messagewrap = selection.append('div').attr('class', 'header fillL');
89100 var message = messagewrap.append('h3').html(_t.html('inspector.choose'));
89101 messagewrap.append('button').attr('class', 'preset-choose').on('click', function () {
89102 dispatch$1.call('cancel', this);
89103 }).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'));
89105 function initialKeydown(d3_event) {
89106 // hack to let delete shortcut work when search is autofocused
89107 if (search.property('value').length === 0 && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
89108 d3_event.preventDefault();
89109 d3_event.stopPropagation();
89110 operationDelete(context, _entityIDs)(); // hack to let undo work when search is autofocused
89111 } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === utilKeybinding.keyCodes.z) {
89112 d3_event.preventDefault();
89113 d3_event.stopPropagation();
89115 } else if (!d3_event.ctrlKey && !d3_event.metaKey) {
89116 // don't check for delete/undo hack on future keydown events
89117 select(this).on('keydown', keydown);
89118 keydown.call(this, d3_event);
89122 function keydown(d3_event) {
89124 if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string
89125 search.node().selectionStart === search.property('value').length) {
89126 d3_event.preventDefault();
89127 d3_event.stopPropagation(); // move focus to the first item in the preset list
89129 var buttons = list.selectAll('.preset-list-button');
89130 if (!buttons.empty()) buttons.nodes()[0].focus();
89134 function keypress(d3_event) {
89136 var value = search.property('value');
89138 if (d3_event.keyCode === 13 && // ↩ Return
89140 list.selectAll('.preset-list-item:first-child').each(function (d) {
89141 d.choose.call(this);
89146 function inputevent() {
89147 var value = search.property('value');
89148 list.classed('filtered', value.length);
89149 var extent = combinedEntityExtent();
89150 var results, messageText;
89152 if (value.length && extent) {
89153 var center = extent.center();
89154 var countryCode = iso1A2Code(center);
89155 results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
89156 messageText = _t('inspector.results', {
89157 n: results.collection.length,
89161 results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
89162 messageText = _t('inspector.choose');
89165 list.call(drawList, results);
89166 message.html(messageText);
89169 var searchWrap = selection.append('div').attr('class', 'search-header');
89170 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
89171 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);
89174 search.node().focus(); // Safari 14 doesn't always like to focus immediately,
89175 // so try again on the next pass
89177 setTimeout(function () {
89178 search.node().focus();
89182 var listWrap = selection.append('div').attr('class', 'inspector-body');
89183 var list = listWrap.append('div').attr('class', 'preset-list').call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
89184 context.features().on('change.preset-list', updateForFeatureHiddenState);
89187 function drawList(list, presets) {
89188 presets = presets.matchAllGeometry(entityGeometries());
89189 var collection = presets.collection.reduce(function (collection, preset) {
89190 if (!preset) return collection;
89192 if (preset.members) {
89193 if (preset.members.collection.filter(function (preset) {
89194 return preset.addable();
89196 collection.push(CategoryItem(preset));
89198 } else if (preset.addable()) {
89199 collection.push(PresetItem(preset));
89204 var items = list.selectAll('.preset-list-item').data(collection, function (d) {
89205 return d.preset.id;
89208 items.exit().remove();
89209 items.enter().append('div').attr('class', function (item) {
89210 return 'preset-list-item preset-' + item.preset.id.replace('/', '-');
89211 }).classed('current', function (item) {
89212 return _currentPresets.indexOf(item.preset) !== -1;
89213 }).each(function (item) {
89214 select(this).call(item);
89215 }).style('opacity', 0).transition().style('opacity', 1);
89216 updateForFeatureHiddenState();
89219 function itemKeydown(d3_event) {
89220 // the actively focused item
89221 var item = select(this.closest('.preset-list-item'));
89222 var parentItem = select(item.node().parentNode.closest('.preset-list-item')); // arrow down, move focus to the next, lower item
89224 if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
89225 d3_event.preventDefault();
89226 d3_event.stopPropagation(); // the next item in the list at the same level
89228 var nextItem = select(item.node().nextElementSibling); // if there is no next item in this list
89230 if (nextItem.empty()) {
89231 // if there is a parent item
89232 if (!parentItem.empty()) {
89233 // the item is the last item of a sublist,
89234 // select the next item at the parent level
89235 nextItem = select(parentItem.node().nextElementSibling);
89236 } // if the focused item is expanded
89238 } else if (select(this).classed('expanded')) {
89239 // select the first subitem instead
89240 nextItem = item.select('.subgrid .preset-list-item:first-child');
89243 if (!nextItem.empty()) {
89244 // focus on the next item
89245 nextItem.select('.preset-list-button').node().focus();
89246 } // arrow up, move focus to the previous, higher item
89248 } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
89249 d3_event.preventDefault();
89250 d3_event.stopPropagation(); // the previous item in the list at the same level
89252 var previousItem = select(item.node().previousElementSibling); // if there is no previous item in this list
89254 if (previousItem.empty()) {
89255 // if there is a parent item
89256 if (!parentItem.empty()) {
89257 // the item is the first subitem of a sublist select the parent item
89258 previousItem = parentItem;
89259 } // if the previous item is expanded
89261 } else if (previousItem.select('.preset-list-button').classed('expanded')) {
89262 // select the last subitem of the sublist of the previous item
89263 previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
89266 if (!previousItem.empty()) {
89267 // focus on the previous item
89268 previousItem.select('.preset-list-button').node().focus();
89270 // the focus is at the top of the list, move focus back to the search field
89271 var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
89272 search.node().focus();
89273 } // arrow left, move focus to the parent item if there is one
89275 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
89276 d3_event.preventDefault();
89277 d3_event.stopPropagation(); // if there is a parent item, focus on the parent item
89279 if (!parentItem.empty()) {
89280 parentItem.select('.preset-list-button').node().focus();
89281 } // arrow right, choose this item
89283 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
89284 d3_event.preventDefault();
89285 d3_event.stopPropagation();
89286 item.datum().choose.call(select(this).node());
89290 function CategoryItem(preset) {
89295 function item(selection) {
89296 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap category');
89299 var isExpanded = select(this).classed('expanded');
89300 var iconName = isExpanded ? _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward' : '#iD-icon-down';
89301 select(this).classed('expanded', !isExpanded);
89302 select(this).selectAll('div.label-inner svg.icon use').attr('href', iconName);
89306 var geometries = entityGeometries();
89307 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) {
89308 // right arrow, expand the focused item
89309 if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
89310 d3_event.preventDefault();
89311 d3_event.stopPropagation(); // if the item isn't expanded
89313 if (!select(this).classed('expanded')) {
89314 // toggle expansion (expand the item)
89315 click.call(this, d3_event);
89316 } // left arrow, collapse the focused item
89318 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
89319 d3_event.preventDefault();
89320 d3_event.stopPropagation(); // if the item is expanded
89322 if (select(this).classed('expanded')) {
89323 // toggle expansion (collapse the item)
89324 click.call(this, d3_event);
89327 itemKeydown.call(this, d3_event);
89330 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
89331 label.append('div').attr('class', 'namepart').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')).append('span').html(function () {
89332 return preset.nameLabel() + '…';
89334 box = selection.append('div').attr('class', 'subgrid').style('max-height', '0px').style('opacity', 0);
89335 box.append('div').attr('class', 'arrow');
89336 sublist = box.append('div').attr('class', 'preset-list fillL3');
89339 item.choose = function () {
89340 if (!box || !sublist) return;
89344 box.transition().duration(200).style('opacity', '0').style('max-height', '0px').style('padding-bottom', '0px');
89347 var members = preset.members.matchAllGeometry(entityGeometries());
89348 sublist.call(drawList, members);
89349 box.transition().duration(200).style('opacity', '1').style('max-height', 200 + members.collection.length * 190 + 'px').style('padding-bottom', '10px');
89353 item.preset = preset;
89357 function PresetItem(preset) {
89358 function item(selection) {
89359 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap');
89360 var geometries = entityGeometries();
89361 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);
89362 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
89363 var nameparts = [preset.nameLabel(), preset.subtitleLabel()].filter(Boolean);
89364 label.selectAll('.namepart').data(nameparts).enter().append('div').attr('class', 'namepart').html(function (d) {
89367 wrap.call(item.reference.button);
89368 selection.call(item.reference.body);
89371 item.choose = function () {
89372 if (select(this).classed('disabled')) return;
89374 if (!context.inIntro()) {
89375 _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
89378 context.perform(function (graph) {
89379 for (var i in _entityIDs) {
89380 var entityID = _entityIDs[i];
89381 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
89382 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
89386 }, _t('operations.change_tags.annotation'));
89387 context.validator().validate(); // rerun validation
89389 dispatch$1.call('choose', this, preset);
89392 item.help = function (d3_event) {
89393 d3_event.stopPropagation();
89394 item.reference.toggle();
89397 item.preset = preset;
89398 item.reference = uiTagReference(preset.reference());
89402 function updateForFeatureHiddenState() {
89403 if (!_entityIDs.every(context.hasEntity)) return;
89404 var geometries = entityGeometries();
89405 var button = context.container().selectAll('.preset-list .preset-list-button'); // remove existing tooltips
89407 button.call(uiTooltip().destroyAny);
89408 button.each(function (item, index) {
89409 var hiddenPresetFeaturesId;
89411 for (var i in geometries) {
89412 hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
89413 if (hiddenPresetFeaturesId) break;
89416 var isHiddenPreset = !context.inIntro() && !!hiddenPresetFeaturesId && (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
89417 select(this).classed('disabled', isHiddenPreset);
89419 if (isHiddenPreset) {
89420 var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
89421 select(this).call(uiTooltip().title(_t.html('inspector.hidden_preset.' + (isAutoHidden ? 'zoom' : 'manual'), {
89422 features: _t.html('feature.' + hiddenPresetFeaturesId + '.description')
89423 })).placement(index < 2 ? 'bottom' : 'top'));
89428 presetList.autofocus = function (val) {
89429 if (!arguments.length) return _autofocus;
89434 presetList.entityIDs = function (val) {
89435 if (!arguments.length) return _entityIDs;
89438 if (_entityIDs && _entityIDs.length) {
89439 var presets = _entityIDs.map(function (entityID) {
89440 return _mainPresetIndex.match(context.entity(entityID), context.graph());
89443 presetList.presets(presets);
89449 presetList.presets = function (val) {
89450 if (!arguments.length) return _currentPresets;
89451 _currentPresets = val;
89455 function entityGeometries() {
89458 for (var i in _entityIDs) {
89459 var entityID = _entityIDs[i];
89460 var entity = context.entity(entityID);
89461 var geometry = entity.geometry(context.graph()); // Treat entities on addr:interpolation lines as points, not vertices (#3241)
89463 if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
89464 geometry = 'point';
89467 if (!counts[geometry]) counts[geometry] = 0;
89468 counts[geometry] += 1;
89471 return Object.keys(counts).sort(function (geom1, geom2) {
89472 return counts[geom2] - counts[geom1];
89476 function combinedEntityExtent() {
89477 return _entityIDs.reduce(function (extent, entityID) {
89478 var entity = context.graph().entity(entityID);
89479 return extent.extend(entity.extent(context.graph()));
89483 return utilRebind(presetList, dispatch$1, 'on');
89486 function uiInspector(context) {
89487 var presetList = uiPresetList(context);
89488 var entityEditor = uiEntityEditor(context);
89489 var wrap = select(null),
89490 presetPane = select(null),
89491 editorPane = select(null);
89492 var _state = 'select';
89496 var _newFeature = false;
89498 function inspector(selection) {
89499 presetList.entityIDs(_entityIDs).autofocus(_newFeature).on('choose', inspector.setPreset).on('cancel', function () {
89500 inspector.setPreset();
89502 entityEditor.state(_state).entityIDs(_entityIDs).on('choose', inspector.showList);
89503 wrap = selection.selectAll('.panewrap').data([0]);
89504 var enter = wrap.enter().append('div').attr('class', 'panewrap');
89505 enter.append('div').attr('class', 'preset-list-pane pane');
89506 enter.append('div').attr('class', 'entity-editor-pane pane');
89507 wrap = wrap.merge(enter);
89508 presetPane = wrap.selectAll('.preset-list-pane');
89509 editorPane = wrap.selectAll('.entity-editor-pane');
89511 function shouldDefaultToPresetList() {
89512 // always show the inspector on hover
89513 if (_state !== 'select') return false; // can only change preset on single selection
89515 if (_entityIDs.length !== 1) return false;
89516 var entityID = _entityIDs[0];
89517 var entity = context.hasEntity(entityID);
89518 if (!entity) return false; // default to inspector if there are already tags
89520 if (entity.hasNonGeometryTags()) return false; // prompt to select preset if feature is new and untagged
89522 if (_newFeature) return true; // all existing features except vertices should default to inspector
89524 if (entity.geometry(context.graph()) !== 'vertex') return false; // show vertex relations if any
89526 if (context.graph().parentRelations(entity).length) return false; // show vertex issues if there are any
89528 if (context.validator().getEntityIssues(entityID).length) return false; // show turn retriction editor for junction vertices
89530 if (entity.isHighwayIntersection(context.graph())) return false; // otherwise show preset list for uninteresting vertices
89535 if (shouldDefaultToPresetList()) {
89536 wrap.style('right', '-100%');
89537 editorPane.classed('hide', true);
89538 presetPane.classed('hide', false).call(presetList);
89540 wrap.style('right', '0%');
89541 presetPane.classed('hide', true);
89542 editorPane.classed('hide', false).call(entityEditor);
89545 var footer = selection.selectAll('.footer').data([0]);
89546 footer = footer.enter().append('div').attr('class', 'footer').merge(footer);
89547 footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])));
89550 inspector.showList = function (presets) {
89551 presetPane.classed('hide', false);
89552 wrap.transition().styleTween('right', function () {
89553 return interpolate('0%', '-100%');
89554 }).on('end', function () {
89555 editorPane.classed('hide', true);
89559 presetList.presets(presets);
89562 presetPane.call(presetList.autofocus(true));
89565 inspector.setPreset = function (preset) {
89566 // upon setting multipolygon, go to the area preset list instead of the editor
89567 if (preset && preset.id === 'type/multipolygon') {
89568 presetPane.call(presetList.autofocus(true));
89570 editorPane.classed('hide', false);
89571 wrap.transition().styleTween('right', function () {
89572 return interpolate('-100%', '0%');
89573 }).on('end', function () {
89574 presetPane.classed('hide', true);
89578 entityEditor.presets([preset]);
89581 editorPane.call(entityEditor);
89585 inspector.state = function (val) {
89586 if (!arguments.length) return _state;
89588 entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector
89590 context.container().selectAll('.field-help-body').remove();
89594 inspector.entityIDs = function (val) {
89595 if (!arguments.length) return _entityIDs;
89600 inspector.newFeature = function (val) {
89601 if (!arguments.length) return _newFeature;
89609 function uiSidebar(context) {
89610 var inspector = uiInspector(context);
89611 var dataEditor = uiDataEditor(context);
89612 var noteEditor = uiNoteEditor(context);
89613 var improveOsmEditor = uiImproveOsmEditor(context);
89614 var keepRightEditor = uiKeepRightEditor(context);
89615 var osmoseEditor = uiOsmoseEditor(context);
89619 var _wasData = false;
89620 var _wasNote = false;
89621 var _wasQaItem = false; // use pointer events on supported platforms; fallback to mouse events
89623 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
89625 function sidebar(selection) {
89626 var container = context.container();
89627 var minWidth = 240;
89629 var containerWidth;
89630 var dragOffset; // Set the initial width constraints
89632 selection.style('min-width', minWidth + 'px').style('max-width', '400px').style('width', '33.3333%');
89633 var resizer = selection.append('div').attr('class', 'sidebar-resizer').on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
89634 var downPointerId, lastClientX, containerLocGetter;
89636 function pointerdown(d3_event) {
89637 if (downPointerId) return;
89638 if ('button' in d3_event && d3_event.button !== 0) return;
89639 downPointerId = d3_event.pointerId || 'mouse';
89640 lastClientX = d3_event.clientX;
89641 containerLocGetter = utilFastMouse(container.node()); // offset from edge of sidebar-resizer
89643 dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;
89644 sidebarWidth = selection.node().getBoundingClientRect().width;
89645 containerWidth = container.node().getBoundingClientRect().width;
89646 var widthPct = sidebarWidth / containerWidth * 100;
89647 selection.style('width', widthPct + '%') // lock in current width
89648 .style('max-width', '85%'); // but allow larger widths
89650 resizer.classed('dragging', true);
89651 select(window).on('touchmove.sidebar-resizer', function (d3_event) {
89652 // disable page scrolling while resizing on touch input
89653 d3_event.preventDefault();
89656 }).on(_pointerPrefix + 'move.sidebar-resizer', pointermove).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
89659 function pointermove(d3_event) {
89660 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
89661 d3_event.preventDefault();
89662 var dx = d3_event.clientX - lastClientX;
89663 lastClientX = d3_event.clientX;
89664 var isRTL = _mainLocalizer.textDirection() === 'rtl';
89665 var scaleX = isRTL ? 0 : 1;
89666 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
89667 var x = containerLocGetter(d3_event)[0] - dragOffset;
89668 sidebarWidth = isRTL ? containerWidth - x : x;
89669 var isCollapsed = selection.classed('collapsed');
89670 var shouldCollapse = sidebarWidth < minWidth;
89671 selection.classed('collapsed', shouldCollapse);
89673 if (shouldCollapse) {
89674 if (!isCollapsed) {
89675 selection.style(xMarginProperty, '-400px').style('width', '400px');
89676 context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
89679 var widthPct = sidebarWidth / containerWidth * 100;
89680 selection.style(xMarginProperty, null).style('width', widthPct + '%');
89683 context.ui().onResize([-sidebarWidth * scaleX, 0]);
89685 context.ui().onResize([-dx * scaleX, 0]);
89690 function pointerup(d3_event) {
89691 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
89692 downPointerId = null;
89693 resizer.classed('dragging', false);
89694 select(window).on('touchmove.sidebar-resizer', null).on(_pointerPrefix + 'move.sidebar-resizer', null).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
89697 var featureListWrap = selection.append('div').attr('class', 'feature-list-pane').call(uiFeatureList(context));
89698 var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap');
89700 var hoverModeSelect = function hoverModeSelect(targets) {
89701 context.container().selectAll('.feature-list-item').classed('hover', false);
89703 if (context.selectedIDs().length > 1 && targets && targets.length) {
89704 var elements = context.container().selectAll('.feature-list-item').filter(function (node) {
89705 return targets.indexOf(node) !== -1;
89708 if (!elements.empty()) {
89709 elements.classed('hover', true);
89714 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
89716 function hover(targets) {
89717 var datum = targets && targets.length && targets[0];
89719 if (datum && datum.__featurehash__) {
89720 // hovering on data
89722 sidebar.show(dataEditor.datum(datum));
89723 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89724 } else if (datum instanceof osmNote) {
89725 if (context.mode().id === 'drag-note') return;
89727 var osm = services.osm;
89730 datum = osm.getNote(datum.id); // marker may contain stale data - get latest
89733 sidebar.show(noteEditor.note(datum));
89734 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89735 } else if (datum instanceof QAItem) {
89737 var errService = services[datum.service];
89740 // marker may contain stale data - get latest
89741 datum = errService.getError(datum.id);
89742 } // Currently only three possible services
89747 if (datum.service === 'keepRight') {
89748 errEditor = keepRightEditor;
89749 } else if (datum.service === 'osmose') {
89750 errEditor = osmoseEditor;
89752 errEditor = improveOsmEditor;
89755 context.container().selectAll('.qaItem.' + datum.service).classed('hover', function (d) {
89756 return d.id === datum.id;
89758 sidebar.show(errEditor.error(datum));
89759 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89760 } else if (!_current && datum instanceof osmEntity) {
89761 featureListWrap.classed('inspector-hidden', true);
89762 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', true);
89764 if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
89765 inspector.state('hover').entityIDs([datum.id]).newFeature(false);
89766 inspectorWrap.call(inspector);
89768 } else if (!_current) {
89769 featureListWrap.classed('inspector-hidden', false);
89770 inspectorWrap.classed('inspector-hidden', true);
89771 inspector.state('hide');
89772 } else if (_wasData || _wasNote || _wasQaItem) {
89775 _wasQaItem = false;
89776 context.container().selectAll('.note').classed('hover', false);
89777 context.container().selectAll('.qaItem').classed('hover', false);
89782 sidebar.hover = throttle(hover, 200);
89784 sidebar.intersects = function (extent) {
89785 var rect = selection.node().getBoundingClientRect();
89786 return extent.intersects([context.projection.invert([0, rect.height]), context.projection.invert([rect.width, 0])]);
89789 sidebar.select = function (ids, newFeature) {
89792 if (ids && ids.length) {
89793 var entity = ids.length === 1 && context.entity(ids[0]);
89795 if (entity && newFeature && selection.classed('collapsed')) {
89796 // uncollapse the sidebar
89797 var extent = entity.extent(context.graph());
89798 sidebar.expand(sidebar.intersects(extent));
89801 featureListWrap.classed('inspector-hidden', true);
89802 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', false); // reload the UI even if the ids are the same since the entities
89803 // themselves may have changed
89805 inspector.state('select').entityIDs(ids).newFeature(newFeature);
89806 inspectorWrap.call(inspector);
89808 inspector.state('hide');
89812 sidebar.showPresetList = function () {
89813 inspector.showList();
89816 sidebar.show = function (component, element) {
89817 featureListWrap.classed('inspector-hidden', true);
89818 inspectorWrap.classed('inspector-hidden', true);
89819 if (_current) _current.remove();
89820 _current = selection.append('div').attr('class', 'sidebar-component').call(component, element);
89823 sidebar.hide = function () {
89824 featureListWrap.classed('inspector-hidden', false);
89825 inspectorWrap.classed('inspector-hidden', true);
89826 if (_current) _current.remove();
89830 sidebar.expand = function (moveMap) {
89831 if (selection.classed('collapsed')) {
89832 sidebar.toggle(moveMap);
89836 sidebar.collapse = function (moveMap) {
89837 if (!selection.classed('collapsed')) {
89838 sidebar.toggle(moveMap);
89842 sidebar.toggle = function (moveMap) {
89843 // Don't allow sidebar to toggle when the user is in the walkthrough.
89844 if (context.inIntro()) return;
89845 var isCollapsed = selection.classed('collapsed');
89846 var isCollapsing = !isCollapsed;
89847 var isRTL = _mainLocalizer.textDirection() === 'rtl';
89848 var scaleX = isRTL ? 0 : 1;
89849 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
89850 sidebarWidth = selection.node().getBoundingClientRect().width; // switch from % to px
89852 selection.style('width', sidebarWidth + 'px');
89853 var startMargin, endMargin, lastMargin;
89855 if (isCollapsing) {
89856 startMargin = lastMargin = 0;
89857 endMargin = -sidebarWidth;
89859 startMargin = lastMargin = -sidebarWidth;
89863 selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
89864 var i = d3_interpolateNumber(startMargin, endMargin);
89865 return function (t) {
89866 var dx = lastMargin - Math.round(i(t));
89867 lastMargin = lastMargin - dx;
89868 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
89870 }).on('end', function () {
89871 selection.classed('collapsed', isCollapsing); // switch back from px to %
89873 if (!isCollapsing) {
89874 var containerWidth = container.node().getBoundingClientRect().width;
89875 var widthPct = sidebarWidth / containerWidth * 100;
89876 selection.style(xMarginProperty, null).style('width', widthPct + '%');
89879 }; // toggle the sidebar collapse when double-clicking the resizer
89882 resizer.on('dblclick', function (d3_event) {
89883 d3_event.preventDefault();
89885 if (d3_event.sourceEvent) {
89886 d3_event.sourceEvent.preventDefault();
89890 }); // ensure hover sidebar is closed when zooming out beyond editable zoom
89892 context.map().on('crossEditableZoom.sidebar', function (within) {
89893 if (!within && !selection.select('.inspector-hover').empty()) {
89899 sidebar.showPresetList = function () {};
89901 sidebar.hover = function () {};
89903 sidebar.hover.cancel = function () {};
89905 sidebar.intersects = function () {};
89907 sidebar.select = function () {};
89909 sidebar.show = function () {};
89911 sidebar.hide = function () {};
89913 sidebar.expand = function () {};
89915 sidebar.collapse = function () {};
89917 sidebar.toggle = function () {};
89922 function uiSourceSwitch(context) {
89925 function click(d3_event) {
89926 d3_event.preventDefault();
89927 var osm = context.connection();
89929 if (context.inIntro()) return;
89930 if (context.history().hasChanges() && !window.confirm(_t('source_switch.lose_changes'))) return;
89931 var isLive = select(this).classed('live');
89933 context.enter(modeBrowse(context));
89934 context.history().clearSaved(); // remove saved history
89936 context.flush(); // remove stored data
89938 select(this).html(isLive ? _t.html('source_switch.live') : _t.html('source_switch.dev')).classed('live', isLive).classed('chip', isLive);
89939 osm["switch"](isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event)
89942 var sourceSwitch = function sourceSwitch(selection) {
89943 selection.append('a').attr('href', '#').html(_t.html('source_switch.live')).attr('class', 'live chip').on('click', click);
89946 sourceSwitch.keys = function (_) {
89947 if (!arguments.length) return keys;
89949 return sourceSwitch;
89952 return sourceSwitch;
89955 function uiSpinner(context) {
89956 var osm = context.connection();
89957 return function (selection) {
89958 var img = selection.append('img').attr('src', context.imagePath('loader-black.gif')).style('opacity', 0);
89961 osm.on('loading.spinner', function () {
89962 img.transition().style('opacity', 1);
89963 }).on('loaded.spinner', function () {
89964 img.transition().style('opacity', 0);
89970 function uiSplash(context) {
89971 return function (selection) {
89972 // Exception - if there are restorable changes, skip this splash screen.
89973 // This is because we currently only support one `uiModal` at a time
89974 // and we need to show them `uiRestore`` instead of this one.
89975 if (context.history().hasRestorableChanges()) return; // If user has not seen this version of the privacy policy, show the splash again.
89977 var updateMessage = '';
89978 var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
89979 var showSplash = !corePreferences('sawSplash');
89981 if (sawPrivacyVersion !== context.privacyVersion) {
89982 updateMessage = _t('splash.privacy_update');
89986 if (!showSplash) return;
89987 corePreferences('sawSplash', true);
89988 corePreferences('sawPrivacyVersion', context.privacyVersion); // fetch intro graph data now, while user is looking at the splash screen
89990 _mainFileFetcher.get('intro_graph');
89991 var modalSelection = uiModal(selection);
89992 modalSelection.select('.modal').attr('class', 'modal-splash modal');
89993 var introModal = modalSelection.select('.content').append('div').attr('class', 'fillL');
89994 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('splash.welcome'));
89995 var modalSection = introModal.append('div').attr('class', 'modal-section');
89996 modalSection.append('p').html(_t.html('splash.text', {
89997 version: context.version,
89998 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
89999 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
90001 modalSection.append('p').html(_t.html('splash.privacy', {
90002 updateMessage: updateMessage,
90003 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' + _t('splash.privacy_policy') + '</a>'
90005 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
90006 var walkthrough = buttonWrap.append('button').attr('class', 'walkthrough').on('click', function () {
90007 context.container().call(uiIntro(context));
90008 modalSelection.close();
90010 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
90011 walkthrough.append('div').html(_t.html('splash.walkthrough'));
90012 var startEditing = buttonWrap.append('button').attr('class', 'start-editing').on('click', modalSelection.close);
90013 startEditing.append('svg').attr('class', 'logo logo-features').append('use').attr('xlink:href', '#iD-logo-features');
90014 startEditing.append('div').html(_t.html('splash.start'));
90015 modalSelection.select('button.close').attr('class', 'hide');
90019 function uiStatus(context) {
90020 var osm = context.connection();
90021 return function (selection) {
90024 function update(err, apiStatus) {
90025 selection.html('');
90028 if (apiStatus === 'connectionSwitched') {
90029 // if the connection was just switched, we can't rely on
90030 // the status (we're getting the status of the previous api)
90032 } else if (apiStatus === 'rateLimited') {
90033 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) {
90034 d3_event.preventDefault();
90035 osm.authenticate();
90038 // don't allow retrying too rapidly
90039 var throttledRetry = throttle(function () {
90040 // try loading the visible tiles
90041 context.loadTiles(context.projection); // manually reload the status too in case all visible tiles were already loaded
90043 osm.reloadApiStatus();
90044 }, 2000); // eslint-disable-next-line no-warning-comments
90045 // TODO: nice messages for different error types
90048 selection.html(_t.html('osm_api_status.message.error') + ' ').append('a').attr('href', '#') // let the user manually retry their connection directly
90049 .html(_t.html('osm_api_status.retry')).on('click.retry', function (d3_event) {
90050 d3_event.preventDefault();
90054 } else if (apiStatus === 'readonly') {
90055 selection.html(_t.html('osm_api_status.message.readonly'));
90056 } else if (apiStatus === 'offline') {
90057 selection.html(_t.html('osm_api_status.message.offline'));
90060 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
90063 osm.on('apiStatusChange.uiStatus', update); // reload the status periodically regardless of other factors
90065 window.setInterval(function () {
90066 osm.reloadApiStatus();
90067 }, 90000); // load the initial status in case no OSM data was loaded yet
90069 osm.reloadApiStatus();
90073 function modeDrawArea(context, wayID, startGraph, button) {
90078 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawArea', function () {
90079 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.areas'))();
90081 mode.wayID = wayID;
90083 mode.enter = function () {
90084 context.install(behavior);
90087 mode.exit = function () {
90088 context.uninstall(behavior);
90091 mode.selectedIDs = function () {
90095 mode.activeID = function () {
90096 return behavior && behavior.activeID() || [];
90102 function modeAddArea(context, mode) {
90103 mode.id = 'add-area';
90104 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
90105 var defaultTags = {
90108 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
90110 function actionClose(wayId) {
90111 return function (graph) {
90112 return graph.replace(graph.entity(wayId).close());
90116 function start(loc) {
90117 var startGraph = context.graph();
90118 var node = osmNode({
90124 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
90125 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90128 function startFromWay(loc, edge) {
90129 var startGraph = context.graph();
90130 var node = osmNode({
90136 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({
90140 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90143 function startFromNode(node) {
90144 var startGraph = context.graph();
90148 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
90149 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90152 mode.enter = function () {
90153 context.install(behavior);
90156 mode.exit = function () {
90157 context.uninstall(behavior);
90163 function modeAddLine(context, mode) {
90164 mode.id = 'add-line';
90165 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
90166 var defaultTags = {};
90167 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
90169 function start(loc) {
90170 var startGraph = context.graph();
90171 var node = osmNode({
90177 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id));
90178 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90181 function startFromWay(loc, edge) {
90182 var startGraph = context.graph();
90183 var node = osmNode({
90189 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({
90193 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90196 function startFromNode(node) {
90197 var startGraph = context.graph();
90201 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id));
90202 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90205 mode.enter = function () {
90206 context.install(behavior);
90209 mode.exit = function () {
90210 context.uninstall(behavior);
90216 function modeAddPoint(context, mode) {
90217 mode.id = 'add-point';
90218 var behavior = behaviorDraw(context).on('click', add).on('clickWay', addWay).on('clickNode', addNode).on('cancel', cancel).on('finish', cancel);
90219 var defaultTags = {};
90220 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
90222 function add(loc) {
90223 var node = osmNode({
90227 context.perform(actionAddEntity(node), _t('operations.add.annotation.point'));
90228 enterSelectMode(node);
90231 function addWay(loc, edge) {
90232 var node = osmNode({
90235 context.perform(actionAddMidpoint({
90238 }, node), _t('operations.add.annotation.vertex'));
90239 enterSelectMode(node);
90242 function enterSelectMode(node) {
90243 context.enter(modeSelect(context, [node.id]).newFeature(true));
90246 function addNode(node) {
90247 if (Object.keys(defaultTags).length === 0) {
90248 enterSelectMode(node);
90252 var tags = Object.assign({}, node.tags); // shallow copy
90254 for (var key in defaultTags) {
90255 tags[key] = defaultTags[key];
90258 context.perform(actionChangeTags(node.id, tags), _t('operations.add.annotation.point'));
90259 enterSelectMode(node);
90262 function cancel() {
90263 context.enter(modeBrowse(context));
90266 mode.enter = function () {
90267 context.install(behavior);
90270 mode.exit = function () {
90271 context.uninstall(behavior);
90277 function modeAddNote(context) {
90281 description: _t.html('modes.add_note.description'),
90282 key: _t('modes.add_note.key')
90284 var behavior = behaviorDraw(context).on('click', add).on('cancel', cancel).on('finish', cancel);
90286 function add(loc) {
90287 var osm = services.osm;
90289 var note = osmNote({
90294 osm.replaceNote(note); // force a reraw (there is no history change that would otherwise do this)
90296 context.map().pan([0, 0]);
90297 context.selectedNoteID(note.id).enter(modeSelectNote(context, note.id).newFeature(true));
90300 function cancel() {
90301 context.enter(modeBrowse(context));
90304 mode.enter = function () {
90305 context.install(behavior);
90308 mode.exit = function () {
90309 context.uninstall(behavior);
90315 function uiConflicts(context) {
90316 var dispatch$1 = dispatch('cancel', 'save');
90317 var keybinding = utilKeybinding('conflicts');
90323 var _shownConflictIndex;
90325 function keybindingOn() {
90326 select(document).call(keybinding.on('⎋', cancel, true));
90329 function keybindingOff() {
90330 select(document).call(keybinding.unbind);
90333 function tryAgain() {
90335 dispatch$1.call('save');
90338 function cancel() {
90340 dispatch$1.call('cancel');
90343 function conflicts(selection) {
90345 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
90346 headerEnter.append('button').attr('class', 'fr').on('click', cancel).call(svgIcon('#iD-icon-close'));
90347 headerEnter.append('h3').html(_t.html('save.conflict.header'));
90348 var bodyEnter = selection.selectAll('.body').data([0]).enter().append('div').attr('class', 'body fillL');
90349 var conflictsHelpEnter = bodyEnter.append('div').attr('class', 'conflicts-help').html(_t.html('save.conflict.help')); // Download changes link
90351 var detected = utilDetect();
90352 var changeset = new osmChangeset();
90353 delete changeset.id; // Export without changeset_id
90355 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
90356 var blob = new Blob([data], {
90357 type: 'text/xml;charset=utf-8;'
90359 var fileName = 'changes.osc';
90360 var linkEnter = conflictsHelpEnter.selectAll('.download-changes').append('a').attr('class', 'download-changes');
90362 if (detected.download) {
90363 // All except IE11 and Edge
90364 linkEnter // download the data as a file
90365 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
90368 linkEnter // open data uri in a new tab
90369 .attr('target', '_blank').on('click.download', function () {
90370 navigator.msSaveBlob(blob, fileName);
90374 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('save.conflict.download_changes'));
90375 bodyEnter.append('div').attr('class', 'conflict-container fillL3').call(showConflict, 0);
90376 bodyEnter.append('div').attr('class', 'conflicts-done').attr('opacity', 0).style('display', 'none').html(_t.html('save.conflict.done'));
90377 var buttonsEnter = bodyEnter.append('div').attr('class', 'buttons col12 joined conflicts-buttons');
90378 buttonsEnter.append('button').attr('disabled', _conflictList.length > 1).attr('class', 'action conflicts-button col6').html(_t.html('save.title')).on('click.try_again', tryAgain);
90379 buttonsEnter.append('button').attr('class', 'secondary-action conflicts-button col6').html(_t.html('confirm.cancel')).on('click.cancel', cancel);
90382 function showConflict(selection, index) {
90383 index = utilWrap(index, _conflictList.length);
90384 _shownConflictIndex = index;
90385 var parent = select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed..
90387 if (index === _conflictList.length - 1) {
90388 window.setTimeout(function () {
90389 parent.select('.conflicts-button').attr('disabled', null);
90390 parent.select('.conflicts-done').transition().attr('opacity', 1).style('display', 'block');
90394 var conflict = selection.selectAll('.conflict').data([_conflictList[index]]);
90395 conflict.exit().remove();
90396 var conflictEnter = conflict.enter().append('div').attr('class', 'conflict');
90397 conflictEnter.append('h4').attr('class', 'conflict-count').html(_t.html('save.conflict.count', {
90399 total: _conflictList.length
90401 conflictEnter.append('a').attr('class', 'conflict-description').attr('href', '#').html(function (d) {
90403 }).on('click', function (d3_event, d) {
90404 d3_event.preventDefault();
90405 zoomToEntity(d.id);
90407 var details = conflictEnter.append('div').attr('class', 'conflict-detail-container');
90408 details.append('ul').attr('class', 'conflict-detail-list').selectAll('li').data(function (d) {
90409 return d.details || [];
90410 }).enter().append('li').attr('class', 'conflict-detail-item').html(function (d) {
90413 details.append('div').attr('class', 'conflict-choices').call(addChoices);
90414 details.append('div').attr('class', 'conflict-nav-buttons joined cf').selectAll('button').data(['previous', 'next']).enter().append('button').html(function (d) {
90415 return _t.html('save.conflict.' + d);
90416 }).attr('class', 'conflict-nav-button action col6').attr('disabled', function (d, i) {
90417 return i === 0 && index === 0 || i === 1 && index === _conflictList.length - 1 || null;
90418 }).on('click', function (d3_event, d) {
90419 d3_event.preventDefault();
90420 var container = parent.selectAll('.conflict-container');
90421 var sign = d === 'previous' ? -1 : 1;
90422 container.selectAll('.conflict').remove();
90423 container.call(showConflict, index + sign);
90427 function addChoices(selection) {
90428 var choices = selection.append('ul').attr('class', 'layer-list').selectAll('li').data(function (d) {
90429 return d.choices || [];
90432 var choicesEnter = choices.enter().append('li').attr('class', 'layer');
90433 var labelEnter = choicesEnter.append('label');
90434 labelEnter.append('input').attr('type', 'radio').attr('name', function (d) {
90436 }).on('change', function (d3_event, d) {
90437 var ul = this.parentNode.parentNode.parentNode;
90438 ul.__data__.chosen = d.id;
90439 choose(d3_event, ul, d);
90441 labelEnter.append('span').html(function (d) {
90445 choicesEnter.merge(choices).each(function (d) {
90446 var ul = this.parentNode;
90448 if (ul.__data__.chosen === d.id) {
90449 choose(null, ul, d);
90454 function choose(d3_event, ul, datum) {
90455 if (d3_event) d3_event.preventDefault();
90456 select(ul).selectAll('li').classed('active', function (d) {
90457 return d === datum;
90458 }).selectAll('input').property('checked', function (d) {
90459 return d === datum;
90461 var extent = geoExtent();
90463 entity = context.graph().hasEntity(datum.id);
90464 if (entity) extent._extend(entity.extent(context.graph()));
90466 entity = context.graph().hasEntity(datum.id);
90467 if (entity) extent._extend(entity.extent(context.graph()));
90468 zoomToEntity(datum.id, extent);
90471 function zoomToEntity(id, extent) {
90472 context.surface().selectAll('.hover').classed('hover', false);
90473 var entity = context.graph().hasEntity(id);
90477 context.map().trimmedExtent(extent);
90479 context.map().zoomToEase(entity);
90482 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
90484 } // The conflict list should be an array of objects like:
90487 // name: entityName(local),
90488 // details: merge.conflicts(),
90491 // choice(id, keepMine, forceLocal),
90492 // choice(id, keepTheirs, forceRemote)
90497 conflicts.conflictList = function (_) {
90498 if (!arguments.length) return _conflictList;
90503 conflicts.origChanges = function (_) {
90504 if (!arguments.length) return _origChanges;
90509 conflicts.shownEntityIds = function () {
90510 if (_conflictList && typeof _shownConflictIndex === 'number') {
90511 return [_conflictList[_shownConflictIndex].id];
90517 return utilRebind(conflicts, dispatch$1, 'on');
90520 function uiConfirm(selection) {
90521 var modalSelection = uiModal(selection);
90522 modalSelection.select('.modal').classed('modal-alert', true);
90523 var section = modalSelection.select('.content');
90524 section.append('div').attr('class', 'modal-section header');
90525 section.append('div').attr('class', 'modal-section message-text');
90526 var buttons = section.append('div').attr('class', 'modal-section buttons cf');
90528 modalSelection.okButton = function () {
90529 buttons.append('button').attr('class', 'button ok-button action').on('click.confirm', function () {
90530 modalSelection.remove();
90531 }).html(_t.html('confirm.okay')).node().focus();
90532 return modalSelection;
90535 return modalSelection;
90538 function uiChangesetEditor(context) {
90539 var dispatch$1 = dispatch('change');
90540 var formFields = uiFormFields(context);
90541 var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
90549 function changesetEditor(selection) {
90553 function render(selection) {
90554 var initial = false;
90558 var presets = _mainPresetIndex;
90559 _fieldsArr = [uiField(context, presets.field('comment'), null, {
90562 }), uiField(context, presets.field('source'), null, {
90565 }), uiField(context, presets.field('hashtags'), null, {
90570 _fieldsArr.forEach(function (field) {
90571 field.on('change', function (t, onInput) {
90572 dispatch$1.call('change', field, undefined, t, onInput);
90577 _fieldsArr.forEach(function (field) {
90581 selection.call(formFields.fieldsArr(_fieldsArr));
90584 var commentField = selection.select('.form-field-comment textarea');
90585 var commentNode = commentField.node();
90588 commentNode.focus();
90589 commentNode.select();
90590 } // trigger a 'blur' event so that comment field can be cleaned
90591 // and checked for hashtags, even if retrieved from localstorage
90594 utilTriggerEvent(commentField, 'blur');
90595 var osm = context.connection();
90598 osm.userChangesets(function (err, changesets) {
90600 var comments = changesets.map(function (changeset) {
90601 var comment = changeset.tags.comment;
90606 }).filter(Boolean);
90607 commentField.call(commentCombo.data(utilArrayUniqBy(comments, 'title')));
90610 } // Add warning if comment mentions Google
90613 var hasGoogle = _tags.comment.match(/google/i);
90615 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning').data(hasGoogle ? [0] : []);
90616 commentWarning.exit().transition().duration(200).style('opacity', 0).remove();
90617 var commentEnter = commentWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning comment-warning').style('opacity', 0);
90618 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'));
90619 commentEnter.transition().duration(200).style('opacity', 1);
90622 changesetEditor.tags = function (_) {
90623 if (!arguments.length) return _tags;
90624 _tags = _; // Don't reset _fieldsArr here.
90626 return changesetEditor;
90629 changesetEditor.changesetID = function (_) {
90630 if (!arguments.length) return _changesetID;
90631 if (_changesetID === _) return changesetEditor;
90634 return changesetEditor;
90637 return utilRebind(changesetEditor, dispatch$1, 'on');
90640 function uiSectionChanges(context) {
90641 var detected = utilDetect();
90642 var _discardTags = {};
90643 _mainFileFetcher.get('discarded').then(function (d) {
90645 })["catch"](function () {
90648 var section = uiSection('changes-list', context).label(function () {
90649 var history = context.history();
90650 var summary = history.difference().summary();
90651 return _t('inspector.title_count', {
90652 title: _t.html('commit.changes'),
90653 count: summary.length
90655 }).disclosureContent(renderDisclosureContent);
90657 function renderDisclosureContent(selection) {
90658 var history = context.history();
90659 var summary = history.difference().summary();
90660 var container = selection.selectAll('.commit-section').data([0]);
90661 var containerEnter = container.enter().append('div').attr('class', 'commit-section');
90662 containerEnter.append('ul').attr('class', 'changeset-list');
90663 container = containerEnter.merge(container);
90664 var items = container.select('ul').selectAll('li').data(summary);
90665 var itemsEnter = items.enter().append('li').attr('class', 'change-item');
90666 var buttons = itemsEnter.append('button').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
90667 buttons.each(function (d) {
90668 select(this).call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
90670 buttons.append('span').attr('class', 'change-type').html(function (d) {
90671 return _t.html('commit.' + d.changeType) + ' ';
90673 buttons.append('strong').attr('class', 'entity-type').html(function (d) {
90674 var matched = _mainPresetIndex.match(d.entity, d.graph);
90675 return matched && matched.name() || utilDisplayType(d.entity.id);
90677 buttons.append('span').attr('class', 'entity-name').html(function (d) {
90678 var name = utilDisplayName(d.entity) || '',
90685 return string += ' ' + name;
90687 items = itemsEnter.merge(items); // Download changeset link
90689 var changeset = new osmChangeset().update({
90692 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
90693 delete changeset.id; // Export without chnageset_id
90695 var data = JXON.stringify(changeset.osmChangeJXON(changes));
90696 var blob = new Blob([data], {
90697 type: 'text/xml;charset=utf-8;'
90699 var fileName = 'changes.osc';
90700 var linkEnter = container.selectAll('.download-changes').data([0]).enter().append('a').attr('class', 'download-changes');
90702 if (detected.download) {
90703 // All except IE11 and Edge
90704 linkEnter // download the data as a file
90705 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
90708 linkEnter // open data uri in a new tab
90709 .attr('target', '_blank').on('click.download', function () {
90710 navigator.msSaveBlob(blob, fileName);
90714 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('commit.download_changes'));
90716 function mouseover(d) {
90718 context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed('hover', true);
90722 function mouseout() {
90723 context.surface().selectAll('.hover').classed('hover', false);
90726 function click(d3_event, change) {
90727 if (change.changeType !== 'deleted') {
90728 var entity = change.entity;
90729 context.map().zoomToEase(entity);
90730 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
90738 function uiCommitWarnings(context) {
90739 function commitWarnings(selection) {
90740 var issuesBySeverity = context.validator().getIssuesBySeverity({
90743 includeDisabledRules: true
90746 for (var severity in issuesBySeverity) {
90747 var issues = issuesBySeverity[severity];
90748 var section = severity + '-section';
90749 var issueItem = severity + '-item';
90750 var container = selection.selectAll('.' + section).data(issues.length ? [0] : []);
90751 container.exit().remove();
90752 var containerEnter = container.enter().append('div').attr('class', 'modal-section ' + section + ' fillL2');
90753 containerEnter.append('h3').html(severity === 'warning' ? _t.html('commit.warnings') : _t.html('commit.errors'));
90754 containerEnter.append('ul').attr('class', 'changeset-list');
90755 container = containerEnter.merge(container);
90756 var items = container.select('ul').selectAll('li').data(issues, function (d) {
90759 items.exit().remove();
90760 var itemsEnter = items.enter().append('li').attr('class', issueItem);
90761 var buttons = itemsEnter.append('button').on('mouseover', function (d3_event, d) {
90763 context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed('hover', true);
90765 }).on('mouseout', function () {
90766 context.surface().selectAll('.hover').classed('hover', false);
90767 }).on('click', function (d3_event, d) {
90768 context.validator().focusIssue(d);
90770 buttons.call(svgIcon('#iD-icon-alert', 'pre-text'));
90771 buttons.append('strong').attr('class', 'issue-message');
90772 buttons.filter(function (d) {
90774 }).call(uiTooltip().title(function (d) {
90776 }).placement('top'));
90777 items = itemsEnter.merge(items);
90778 items.selectAll('.issue-message').html(function (d) {
90779 return d.message(context);
90784 return commitWarnings;
90787 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
90788 // from https://stackoverflow.com/a/25575009
90790 var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
90791 function uiCommit(context) {
90792 var dispatch$1 = dispatch('cancel');
90798 var changesetEditor = uiChangesetEditor(context).on('change', changeTags);
90799 var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context).on('change', changeTags).readOnlyTags(readOnlyTags);
90800 var commitChanges = uiSectionChanges(context);
90801 var commitWarnings = uiCommitWarnings(context);
90803 function commit(selection) {
90804 _selection = selection; // Initialize changeset if one does not exist yet.
90806 if (!context.changeset) initChangeset();
90807 loadDerivedChangesetTags();
90808 selection.call(render);
90811 function initChangeset() {
90812 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
90813 var commentDate = +corePreferences('commentDate') || 0;
90814 var currDate = Date.now();
90815 var cutoff = 2 * 86400 * 1000; // 2 days
90817 if (commentDate > currDate || currDate - commentDate > cutoff) {
90818 corePreferences('comment', null);
90819 corePreferences('hashtags', null);
90820 corePreferences('source', null);
90821 } // load in explicitly-set values, if any
90824 if (context.defaultChangesetComment()) {
90825 corePreferences('comment', context.defaultChangesetComment());
90826 corePreferences('commentDate', Date.now());
90829 if (context.defaultChangesetSource()) {
90830 corePreferences('source', context.defaultChangesetSource());
90831 corePreferences('commentDate', Date.now());
90834 if (context.defaultChangesetHashtags()) {
90835 corePreferences('hashtags', context.defaultChangesetHashtags());
90836 corePreferences('commentDate', Date.now());
90839 var detected = utilDetect();
90841 comment: corePreferences('comment') || '',
90842 created_by: context.cleanTagValue('iD ' + context.version),
90843 host: context.cleanTagValue(detected.host),
90844 locale: context.cleanTagValue(_mainLocalizer.localeCode())
90845 }; // call findHashtags initially - this will remove stored
90846 // hashtags if any hashtags are found in the comment - #4304
90848 findHashtags(tags, true);
90849 var hashtags = corePreferences('hashtags');
90852 tags.hashtags = hashtags;
90855 var source = corePreferences('source');
90858 tags.source = source;
90861 var photoOverlaysUsed = context.history().photoOverlaysUsed();
90863 if (photoOverlaysUsed.length) {
90864 var sources = (tags.source || '').split(';'); // include this tag for any photo layer
90866 if (sources.indexOf('streetlevel imagery') === -1) {
90867 sources.push('streetlevel imagery');
90868 } // add the photo overlays used during editing as sources
90871 photoOverlaysUsed.forEach(function (photoOverlay) {
90872 if (sources.indexOf(photoOverlay) === -1) {
90873 sources.push(photoOverlay);
90876 tags.source = context.cleanTagValue(sources.join(';'));
90879 context.changeset = new osmChangeset({
90882 } // Calculates read-only metadata tags based on the user's editing session and applies
90883 // them to the changeset.
90886 function loadDerivedChangesetTags() {
90887 var osm = context.connection();
90889 var tags = Object.assign({}, context.changeset.tags); // shallow copy
90890 // assign tags for imagery used
90892 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
90893 tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes
90895 var osmClosed = osm.getClosedIDs();
90898 if (osmClosed.length) {
90899 tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
90902 if (services.keepRight) {
90903 var krClosed = services.keepRight.getClosedIDs();
90905 if (krClosed.length) {
90906 tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
90910 if (services.improveOSM) {
90911 var iOsmClosed = services.improveOSM.getClosedCounts();
90913 for (itemType in iOsmClosed) {
90914 tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
90918 if (services.osmose) {
90919 var osmoseClosed = services.osmose.getClosedCounts();
90921 for (itemType in osmoseClosed) {
90922 tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
90924 } // remove existing issue counts
90927 for (var key in tags) {
90928 if (key.match(/(^warnings:)|(^resolved:)/)) {
90933 function addIssueCounts(issues, prefix) {
90934 var issuesByType = utilArrayGroupBy(issues, 'type');
90936 for (var issueType in issuesByType) {
90937 var issuesOfType = issuesByType[issueType];
90939 if (issuesOfType[0].subtype) {
90940 var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
90942 for (var issueSubtype in issuesBySubtype) {
90943 var issuesOfSubtype = issuesBySubtype[issueSubtype];
90944 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
90947 tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
90950 } // add counts of warnings generated by the user's edits
90953 var warnings = context.validator().getIssuesBySeverity({
90956 includeIgnored: true,
90957 includeDisabledRules: true
90959 addIssueCounts(warnings, 'warnings'); // add counts of issues resolved by the user's edits
90961 var resolvedIssues = context.validator().getResolvedIssues();
90962 addIssueCounts(resolvedIssues, 'resolved');
90963 context.changeset = context.changeset.update({
90968 function render(selection) {
90969 var osm = context.connection();
90971 var header = selection.selectAll('.header').data([0]);
90972 var headerTitle = header.enter().append('div').attr('class', 'header fillL');
90973 headerTitle.append('div').append('h3').html(_t.html('commit.title'));
90974 headerTitle.append('button').attr('class', 'close').on('click', function () {
90975 dispatch$1.call('cancel', this);
90976 }).call(svgIcon('#iD-icon-close'));
90977 var body = selection.selectAll('.body').data([0]);
90978 body = body.enter().append('div').attr('class', 'body').merge(body); // Changeset Section
90980 var changesetSection = body.selectAll('.changeset-editor').data([0]);
90981 changesetSection = changesetSection.enter().append('div').attr('class', 'modal-section changeset-editor').merge(changesetSection);
90982 changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)); // Warnings
90984 body.call(commitWarnings); // Upload Explanation
90986 var saveSection = body.selectAll('.save-section').data([0]);
90987 saveSection = saveSection.enter().append('div').attr('class', 'modal-section save-section fillL').merge(saveSection);
90988 var prose = saveSection.selectAll('.commit-info').data([0]);
90990 if (prose.enter().size()) {
90991 // first time, make sure to update user details in prose
90992 _userDetails = null;
90995 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()
90996 // if needed, because it can trigger a style recalculation
90998 osm.userDetails(function (err, user) {
91000 if (_userDetails === user) return; // no change
91002 _userDetails = user;
91003 var userLink = select(document.createElement('div'));
91005 if (user.image_url) {
91006 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
91009 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
91010 prose.html(_t.html('commit.upload_explanation_with_user', {
91011 user: userLink.html()
91013 }); // Request Review
91015 var requestReview = saveSection.selectAll('.request-review').data([0]); // Enter
91017 var requestReviewEnter = requestReview.enter().append('div').attr('class', 'request-review');
91018 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
91019 var labelEnter = requestReviewEnter.append('label').attr('for', requestReviewDomId);
91020 labelEnter.append('input').attr('type', 'checkbox').attr('id', requestReviewDomId);
91021 labelEnter.append('span').html(_t.html('commit.request_review')); // Update
91023 requestReview = requestReview.merge(requestReviewEnter);
91024 var requestReviewInput = requestReview.selectAll('input').property('checked', isReviewRequested(context.changeset.tags)).on('change', toggleRequestReview); // Buttons
91026 var buttonSection = saveSection.selectAll('.buttons').data([0]); // enter
91028 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons fillL');
91029 buttonEnter.append('button').attr('class', 'secondary-action button cancel-button').append('span').attr('class', 'label').html(_t.html('commit.cancel'));
91030 var uploadButton = buttonEnter.append('button').attr('class', 'action button save-button');
91031 uploadButton.append('span').attr('class', 'label').html(_t.html('commit.save'));
91032 var uploadBlockerTooltipText = getUploadBlockerMessage(); // update
91034 buttonSection = buttonSection.merge(buttonEnter);
91035 buttonSection.selectAll('.cancel-button').on('click.cancel', function () {
91036 dispatch$1.call('cancel', this);
91038 buttonSection.selectAll('.save-button').classed('disabled', uploadBlockerTooltipText !== null).on('click.save', function () {
91039 if (!select(this).classed('disabled')) {
91040 this.blur(); // avoid keeping focus on the button - #4641
91042 for (var key in context.changeset.tags) {
91043 // remove any empty keys before upload
91044 if (!key) delete context.changeset.tags[key];
91047 context.uploader().save(context.changeset);
91049 }); // remove any existing tooltip
91051 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
91053 if (uploadBlockerTooltipText) {
91054 buttonSection.selectAll('.save-button').call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
91055 } // Raw Tag Editor
91058 var tagSection = body.selectAll('.tag-section.raw-tag-editor').data([0]);
91059 tagSection = tagSection.enter().append('div').attr('class', 'modal-section tag-section raw-tag-editor').merge(tagSection);
91060 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
91062 var changesSection = body.selectAll('.commit-changes-section').data([0]);
91063 changesSection = changesSection.enter().append('div').attr('class', 'modal-section commit-changes-section').merge(changesSection); // Change summary
91065 changesSection.call(commitChanges.render);
91067 function toggleRequestReview() {
91068 var rr = requestReviewInput.property('checked');
91070 review_requested: rr ? 'yes' : undefined
91072 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
91077 function getUploadBlockerMessage() {
91078 var errors = context.validator().getIssuesBySeverity({
91083 if (errors.length) {
91084 return _t('commit.outstanding_errors_message', {
91085 count: errors.length
91088 var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
91090 if (!hasChangesetComment) {
91091 return _t('commit.comment_needed_message');
91098 function changeTags(_, changed, onInput) {
91099 if (changed.hasOwnProperty('comment')) {
91100 if (changed.comment === undefined) {
91101 changed.comment = '';
91105 corePreferences('comment', changed.comment);
91106 corePreferences('commentDate', Date.now());
91110 if (changed.hasOwnProperty('source')) {
91111 if (changed.source === undefined) {
91112 corePreferences('source', null);
91113 } else if (!onInput) {
91114 corePreferences('source', changed.source);
91115 corePreferences('commentDate', Date.now());
91117 } // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
91120 updateChangeset(changed, onInput);
91123 _selection.call(render);
91127 function findHashtags(tags, commentOnly) {
91128 var detectedHashtags = commentHashtags();
91130 if (detectedHashtags.length) {
91131 // always remove stored hashtags if there are hashtags in the comment - #4304
91132 corePreferences('hashtags', null);
91135 if (!detectedHashtags.length || !commentOnly) {
91136 detectedHashtags = detectedHashtags.concat(hashtagHashtags());
91139 var allLowerCase = new Set();
91140 return detectedHashtags.filter(function (hashtag) {
91141 // Compare tags as lowercase strings, but keep original case tags
91142 var lowerCase = hashtag.toLowerCase();
91144 if (!allLowerCase.has(lowerCase)) {
91145 allLowerCase.add(lowerCase);
91150 }); // Extract hashtags from `comment`
91152 function commentHashtags() {
91153 var matches = (tags.comment || '').replace(/http\S*/g, '') // drop anything that looks like a URL - #4289
91154 .match(hashtagRegex);
91155 return matches || [];
91156 } // Extract and clean hashtags from `hashtags`
91159 function hashtagHashtags() {
91160 var matches = (tags.hashtags || '').split(/[,;\s]+/).map(function (s) {
91161 if (s[0] !== '#') {
91166 var matched = s.match(hashtagRegex);
91167 return matched && matched[0];
91168 }).filter(Boolean); // exclude falsy
91170 return matches || [];
91174 function isReviewRequested(tags) {
91175 var rr = tags.review_requested;
91176 if (rr === undefined) return false;
91177 rr = rr.trim().toLowerCase();
91178 return !(rr === '' || rr === 'no');
91181 function updateChangeset(changed, onInput) {
91182 var tags = Object.assign({}, context.changeset.tags); // shallow copy
91184 Object.keys(changed).forEach(function (k) {
91185 var v = changed[k];
91186 k = context.cleanTagKey(k);
91187 if (readOnlyTags.indexOf(k) !== -1) return;
91189 if (v === undefined) {
91191 } else if (onInput) {
91194 tags[k] = context.cleanTagValue(v);
91199 // when changing the comment, override hashtags with any found in comment.
91200 var commentOnly = changed.hasOwnProperty('comment') && changed.comment !== '';
91201 var arr = findHashtags(tags, commentOnly);
91204 tags.hashtags = context.cleanTagValue(arr.join(';'));
91205 corePreferences('hashtags', tags.hashtags);
91207 delete tags.hashtags;
91208 corePreferences('hashtags', null);
91210 } // always update userdetails, just in case user reauthenticates as someone else
91213 if (_userDetails && _userDetails.changesets_count !== undefined) {
91214 var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283
91216 tags.changesets_count = String(changesetsCount); // first 100 edits - new user
91218 if (changesetsCount <= 100) {
91220 s = corePreferences('walkthrough_completed');
91223 tags['ideditor:walkthrough_completed'] = s;
91226 s = corePreferences('walkthrough_progress');
91229 tags['ideditor:walkthrough_progress'] = s;
91232 s = corePreferences('walkthrough_started');
91235 tags['ideditor:walkthrough_started'] = s;
91239 delete tags.changesets_count;
91242 if (!fastDeepEqual(context.changeset.tags, tags)) {
91243 context.changeset = context.changeset.update({
91249 commit.reset = function () {
91250 context.changeset = null;
91253 return utilRebind(commit, dispatch$1, 'on');
91256 var globalIsFinite = global_1.isFinite;
91258 // `Number.isFinite` method
91259 // https://tc39.github.io/ecma262/#sec-number.isfinite
91260 var numberIsFinite = Number.isFinite || function isFinite(it) {
91261 return typeof it == 'number' && globalIsFinite(it);
91264 // `Number.isFinite` method
91265 // https://tc39.github.io/ecma262/#sec-number.isfinite
91266 _export({ target: 'Number', stat: true }, { isFinite: numberIsFinite });
91268 var RADIUS = 6378137;
91269 var FLATTENING = 1 / 298.257223563;
91270 var POLAR_RADIUS$1 = 6356752.3142;
91273 FLATTENING: FLATTENING,
91274 POLAR_RADIUS: POLAR_RADIUS$1
91277 var geometry_1 = geometry;
91278 var ring = ringArea;
91280 function geometry(_) {
91286 return polygonArea(_.coordinates);
91288 case 'MultiPolygon':
91289 for (i = 0; i < _.coordinates.length; i++) {
91290 area += polygonArea(_.coordinates[i]);
91298 case 'MultiLineString':
91301 case 'GeometryCollection':
91302 for (i = 0; i < _.geometries.length; i++) {
91303 area += geometry(_.geometries[i]);
91310 function polygonArea(coords) {
91313 if (coords && coords.length > 0) {
91314 area += Math.abs(ringArea(coords[0]));
91316 for (var i = 1; i < coords.length; i++) {
91317 area -= Math.abs(ringArea(coords[i]));
91324 * Calculate the approximate area of the polygon were it projected onto
91325 * the earth. Note that this area will be positive if ring is oriented
91326 * clockwise, otherwise it will be negative.
91329 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
91330 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
91331 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
91334 * {float} The approximate signed geodesic area of the polygon in square
91339 function ringArea(coords) {
91348 coordsLength = coords.length;
91350 if (coordsLength > 2) {
91351 for (i = 0; i < coordsLength; i++) {
91352 if (i === coordsLength - 2) {
91354 lowerIndex = coordsLength - 2;
91355 middleIndex = coordsLength - 1;
91357 } else if (i === coordsLength - 1) {
91359 lowerIndex = coordsLength - 1;
91365 middleIndex = i + 1;
91366 upperIndex = i + 2;
91369 p1 = coords[lowerIndex];
91370 p2 = coords[middleIndex];
91371 p3 = coords[upperIndex];
91372 area += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]));
91375 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
91382 return _ * Math.PI / 180;
91385 var geojsonArea = {
91386 geometry: geometry_1,
91390 function toRadians(angleInDegrees) {
91391 return angleInDegrees * Math.PI / 180;
91394 function toDegrees(angleInRadians) {
91395 return angleInRadians * 180 / Math.PI;
91398 function offset(c1, distance, bearing) {
91399 var lat1 = toRadians(c1[1]);
91400 var lon1 = toRadians(c1[0]);
91401 var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
91403 var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
91404 var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
91405 return [toDegrees(lon), toDegrees(lat)];
91408 function validateCenter(center) {
91409 var validCenterLengths = [2, 3];
91411 if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
91412 throw new Error("ERROR! Center has to be an array of length two or three");
91415 var _center = _slicedToArray(center, 2),
91419 if (typeof lng !== "number" || typeof lat !== "number") {
91420 throw new Error("ERROR! Longitude and Latitude has to be numbers but where ".concat(_typeof(lng), " and ").concat(_typeof(lat)));
91423 if (lng > 180 || lng < -180) {
91424 throw new Error("ERROR! Longitude has to be between -180 and 180 but was ".concat(lng));
91427 if (lat > 90 || lat < -90) {
91428 throw new Error("ERROR! Latitude has to be between -90 and 90 but was ".concat(lat));
91432 function validateRadius(radius) {
91433 if (typeof radius !== "number") {
91434 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(_typeof(radius)));
91438 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(radius));
91442 function validateNumberOfSegments(numberOfSegments) {
91443 if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
91444 throw new Error("ERROR! Number of segments has to be a number but was: ".concat(_typeof(numberOfSegments)));
91447 if (numberOfSegments < 3) {
91448 throw new Error("ERROR! Number of segments has to be at least 3 but was: ".concat(numberOfSegments));
91452 function validateInput(_ref) {
91453 var center = _ref.center,
91454 radius = _ref.radius,
91455 numberOfSegments = _ref.numberOfSegments;
91456 validateCenter(center);
91457 validateRadius(radius);
91458 validateNumberOfSegments(numberOfSegments);
91461 var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
91462 var n = numberOfSegments ? numberOfSegments : 32; // validateInput() throws error on invalid input and do nothing on valid input
91467 numberOfSegments: numberOfSegments
91469 var coordinates = [];
91471 for (var i = 0; i < n; ++i) {
91472 coordinates.push(offset(center, radius, 2 * Math.PI * -i / n));
91475 coordinates.push(coordinates[0]);
91478 coordinates: [coordinates]
91482 // `Number.EPSILON` constant
91483 // https://tc39.github.io/ecma262/#sec-number.epsilon
91484 _export({ target: 'Number', stat: true }, {
91485 EPSILON: Math.pow(2, -52)
91490 * Fast Splay tree for Node and browser
91492 * @author Alexander Milevski <info@w8r.name>
91496 var Node$1 = function Node(key, data) {
91497 _classCallCheck(this, Node);
91505 /* follows "An implementation of top-down splaying"
91506 * by D. Sleator <sleator@cs.cmu.edu> March 1992
91510 function DEFAULT_COMPARE$1(a, b) {
91511 return a > b ? 1 : a < b ? -1 : 0;
91514 * Simple top down splay, not requiring i to be in the tree t.
91518 function splay(i, t, comparator) {
91519 var N = new Node$1(null, null);
91524 var cmp = comparator(i, t.key); //if (i < t.key) {
91527 if (t.left === null) break; //if (i < t.left.key) {
91529 if (comparator(i, t.left.key) < 0) {
91536 if (t.left === null) break;
91543 t = t.left; //} else if (i > t.key) {
91544 } else if (cmp > 0) {
91545 if (t.right === null) break; //if (i > t.right.key) {
91547 if (comparator(i, t.right.key) > 0) {
91554 if (t.right === null) break;
91574 function _insert(i, data, t, comparator) {
91575 var node = new Node$1(i, data);
91578 node.left = node.right = null;
91582 t = splay(i, t, comparator);
91583 var cmp = comparator(i, t.key);
91586 node.left = t.left;
91589 } else if (cmp >= 0) {
91590 node.right = t.right;
91598 function _split(key, v, comparator) {
91603 v = splay(key, v, comparator);
91604 var cmp = comparator(v.key, key);
91609 } else if (cmp < 0) {
91626 function merge$4(left, right, comparator) {
91627 if (right === null) return left;
91628 if (left === null) return right;
91629 right = splay(left.key, right, comparator);
91634 * Prints level of the tree
91638 function printRow(root, prefix, isTail, out, printNode) {
91640 out("".concat(prefix).concat(isTail ? '└── ' : '├── ').concat(printNode(root), "\n"));
91641 var indent = prefix + (isTail ? ' ' : '│ ');
91642 if (root.left) printRow(root.left, indent, false, out, printNode);
91643 if (root.right) printRow(root.right, indent, true, out, printNode);
91647 var Tree = /*#__PURE__*/function () {
91649 var comparator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_COMPARE$1;
91651 _classCallCheck(this, Tree);
91655 this._comparator = comparator;
91658 * Inserts a key, allows duplicates
91662 _createClass(Tree, [{
91664 value: function insert(key, data) {
91666 return this._root = _insert(key, data, this._root, this._comparator);
91669 * Adds a key, if it is not present in the tree
91674 value: function add(key, data) {
91675 var node = new Node$1(key, data);
91677 if (this._root === null) {
91678 node.left = node.right = null;
91683 var comparator = this._comparator;
91684 var t = splay(key, this._root, comparator);
91685 var cmp = comparator(key, t.key);
91686 if (cmp === 0) this._root = t;else {
91688 node.left = t.left;
91691 } else if (cmp > 0) {
91692 node.right = t.right;
91704 * @return {Node|null}
91709 value: function remove(key) {
91710 this._root = this._remove(key, this._root, this._comparator);
91713 * Deletes i from the tree if it's there
91718 value: function _remove(i, t, comparator) {
91720 if (t === null) return null;
91721 t = splay(i, t, comparator);
91722 var cmp = comparator(i, t.key);
91726 if (t.left === null) {
91729 x = splay(i, t.left, comparator);
91738 /* It wasn't there */
91741 * Removes and returns the node with smallest key
91746 value: function pop() {
91747 var node = this._root;
91750 while (node.left) {
91754 this._root = splay(node.key, this._root, this._comparator);
91755 this._root = this._remove(node.key, this._root, this._comparator);
91765 * Find without splaying
91770 value: function findStatic(key) {
91771 var current = this._root;
91772 var compare = this._comparator;
91775 var cmp = compare(key, current.key);
91776 if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right;
91783 value: function find(key) {
91785 this._root = splay(key, this._root, this._comparator);
91786 if (this._comparator(key, this._root.key) !== 0) return null;
91793 value: function contains(key) {
91794 var current = this._root;
91795 var compare = this._comparator;
91798 var cmp = compare(key, current.key);
91799 if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right;
91806 value: function forEach(visitor, ctx) {
91807 var current = this._root;
91809 /* Initialize stack s */
91814 if (current !== null) {
91816 current = current.left;
91818 if (Q.length !== 0) {
91820 visitor.call(ctx, current);
91821 current = current.right;
91822 } else done = true;
91829 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
91834 value: function range(low, high, fn, ctx) {
91836 var compare = this._comparator;
91837 var node = this._root;
91840 while (Q.length !== 0 || node) {
91846 cmp = compare(node.key, high);
91850 } else if (compare(node.key, low) >= 0) {
91851 if (fn.call(ctx, node)) return this; // stop if smth is returned
91861 * Returns array of keys
91866 value: function keys() {
91868 this.forEach(function (_ref) {
91869 var key = _ref.key;
91870 return keys.push(key);
91875 * Returns array of all the data in the nodes
91880 value: function values() {
91882 this.forEach(function (_ref2) {
91883 var data = _ref2.data;
91884 return values.push(data);
91890 value: function min() {
91891 if (this._root) return this.minNode(this._root).key;
91896 value: function max() {
91897 if (this._root) return this.maxNode(this._root).key;
91902 value: function minNode() {
91903 var t = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
91904 if (t) while (t.left) {
91911 value: function maxNode() {
91912 var t = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
91913 if (t) while (t.right) {
91919 * Returns node at given index
91924 value: function at(index) {
91925 var current = this._root;
91933 current = current.left;
91935 if (Q.length > 0) {
91937 if (i === index) return current;
91939 current = current.right;
91940 } else done = true;
91948 value: function next(d) {
91949 var root = this._root;
91950 var successor = null;
91953 successor = d.right;
91955 while (successor.left) {
91956 successor = successor.left;
91962 var comparator = this._comparator;
91965 var cmp = comparator(d.key, root.key);
91966 if (cmp === 0) break;else if (cmp < 0) {
91969 } else root = root.right;
91976 value: function prev(d) {
91977 var root = this._root;
91978 var predecessor = null;
91980 if (d.left !== null) {
91981 predecessor = d.left;
91983 while (predecessor.right) {
91984 predecessor = predecessor.right;
91987 return predecessor;
91990 var comparator = this._comparator;
91993 var cmp = comparator(d.key, root.key);
91994 if (cmp === 0) break;else if (cmp < 0) root = root.left;else {
91995 predecessor = root;
92000 return predecessor;
92004 value: function clear() {
92011 value: function toList() {
92012 return _toList(this._root);
92015 * Bulk-load items. Both array have to be same size
92020 value: function load(keys) {
92021 var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
92022 var presort = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
92023 var size = keys.length;
92024 var comparator = this._comparator; // sort if needed
92026 if (presort) sort$1(keys, values, 0, size - 1, comparator);
92028 if (this._root === null) {
92030 this._root = loadRecursive$1(keys, values, 0, size);
92033 // that re-builds the whole tree from two in-order traversals
92034 var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
92035 size = this._size + size;
92036 this._root = sortedListToBST({
92045 value: function isEmpty() {
92046 return this._root === null;
92050 value: function toString() {
92051 var printNode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function (n) {
92052 return String(n.key);
92055 printRow(this._root, '', true, function (v) {
92056 return out.push(v);
92058 return out.join('');
92062 value: function update(key, newKey, newData) {
92063 var comparator = this._comparator;
92065 var _split2 = _split(key, this._root, comparator),
92066 left = _split2.left,
92067 right = _split2.right;
92069 if (comparator(key, newKey) < 0) {
92070 right = _insert(newKey, newData, right, comparator);
92072 left = _insert(newKey, newData, left, comparator);
92075 this._root = merge$4(left, right, comparator);
92079 value: function split(key) {
92080 return _split(key, this._root, this._comparator);
92084 get: function get() {
92089 get: function get() {
92097 function loadRecursive$1(keys, values, start, end) {
92098 var size = end - start;
92101 var middle = start + Math.floor(size / 2);
92102 var key = keys[middle];
92103 var data = values[middle];
92104 var node = new Node$1(key, data);
92105 node.left = loadRecursive$1(keys, values, start, middle);
92106 node.right = loadRecursive$1(keys, values, middle + 1, end);
92113 function createList(keys, values) {
92114 var head = new Node$1(null, null);
92117 for (var i = 0; i < keys.length; i++) {
92118 p = p.next = new Node$1(keys[i], values[i]);
92125 function _toList(root) {
92126 var current = root;
92129 var head = new Node$1(null, null);
92135 current = current.left;
92137 if (Q.length > 0) {
92138 current = p = p.next = Q.pop();
92139 current = current.right;
92140 } else done = true;
92144 p.next = null; // that'll work even if the tree was empty
92149 function sortedListToBST(list, start, end) {
92150 var size = end - start;
92153 var middle = start + Math.floor(size / 2);
92154 var left = sortedListToBST(list, start, middle);
92155 var root = list.head;
92157 list.head = list.head.next;
92158 root.right = sortedListToBST(list, middle + 1, end);
92165 function mergeLists(l1, l2, compare) {
92166 var head = new Node$1(null, null); // dummy
92172 while (p1 !== null && p2 !== null) {
92173 if (compare(p1.key, p2.key) < 0) {
92186 } else if (p2 !== null) {
92193 function sort$1(keys, values, left, right, compare) {
92194 if (left >= right) return;
92195 var pivot = keys[left + right >> 1];
92202 } while (compare(keys[i], pivot) < 0);
92206 } while (compare(keys[j], pivot) > 0);
92213 values[i] = values[j];
92217 sort$1(keys, values, left, j, compare);
92218 sort$1(keys, values, j + 1, right, compare);
92221 function _classCallCheck$1(instance, Constructor) {
92222 if (!(instance instanceof Constructor)) {
92223 throw new TypeError("Cannot call a class as a function");
92227 function _defineProperties$1(target, props) {
92228 for (var i = 0; i < props.length; i++) {
92229 var descriptor = props[i];
92230 descriptor.enumerable = descriptor.enumerable || false;
92231 descriptor.configurable = true;
92232 if ("value" in descriptor) descriptor.writable = true;
92233 Object.defineProperty(target, descriptor.key, descriptor);
92237 function _createClass$1(Constructor, protoProps, staticProps) {
92238 if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
92239 if (staticProps) _defineProperties$1(Constructor, staticProps);
92240 return Constructor;
92243 * A bounding box has the format:
92245 * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
92250 var isInBbox = function isInBbox(bbox, point) {
92251 return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
92253 /* Returns either null, or a bbox (aka an ordered pair of points)
92254 * If there is only one point of overlap, a bbox with identical points
92255 * will be returned */
92258 var getBboxOverlap = function getBboxOverlap(b1, b2) {
92259 // check if the bboxes overlap at all
92260 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
92262 var lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
92263 var upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x; // find the middle two Y values
92265 var lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
92266 var upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y; // put those middle values together to get the overlap
92279 /* Javascript doesn't do integer math. Everything is
92280 * floating point with percision Number.EPSILON.
92282 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
92286 var epsilon$2 = Number.EPSILON; // IE Polyfill
92288 if (epsilon$2 === undefined) epsilon$2 = Math.pow(2, -52);
92289 var EPSILON_SQ = epsilon$2 * epsilon$2;
92290 /* FLP comparator */
92292 var cmp = function cmp(a, b) {
92293 // check if they're both 0
92294 if (-epsilon$2 < a && a < epsilon$2) {
92295 if (-epsilon$2 < b && b < epsilon$2) {
92298 } // check if they're flp equal
92303 if (ab * ab < EPSILON_SQ * a * b) {
92305 } // normal comparison
92308 return a < b ? -1 : 1;
92311 * This class rounds incoming values sufficiently so that
92312 * floating points problems are, for the most part, avoided.
92314 * Incoming points are have their x & y values tested against
92315 * all previously seen x & y values. If either is 'too close'
92316 * to a previously seen value, it's value is 'snapped' to the
92317 * previously seen value.
92319 * All points should be rounded by this class before being
92320 * stored in any data structures in the rest of this algorithm.
92324 var PtRounder = /*#__PURE__*/function () {
92325 function PtRounder() {
92326 _classCallCheck$1(this, PtRounder);
92331 _createClass$1(PtRounder, [{
92333 value: function reset() {
92334 this.xRounder = new CoordRounder();
92335 this.yRounder = new CoordRounder();
92339 value: function round(x, y) {
92341 x: this.xRounder.round(x),
92342 y: this.yRounder.round(y)
92350 var CoordRounder = /*#__PURE__*/function () {
92351 function CoordRounder() {
92352 _classCallCheck$1(this, CoordRounder);
92354 this.tree = new Tree(); // preseed with 0 so we don't end up with values < Number.EPSILON
92357 } // Note: this can rounds input values backwards or forwards.
92358 // You might ask, why not restrict this to just rounding
92359 // forwards? Wouldn't that allow left endpoints to always
92360 // remain left endpoints during splitting (never change to
92361 // right). No - it wouldn't, because we snap intersections
92362 // to endpoints (to establish independence from the segment
92363 // angle for t-intersections).
92366 _createClass$1(CoordRounder, [{
92368 value: function round(coord) {
92369 var node = this.tree.add(coord);
92370 var prevNode = this.tree.prev(node);
92372 if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
92373 this.tree.remove(coord);
92374 return prevNode.key;
92377 var nextNode = this.tree.next(node);
92379 if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
92380 this.tree.remove(coord);
92381 return nextNode.key;
92388 return CoordRounder;
92389 }(); // singleton available by import
92392 var rounder = new PtRounder();
92393 /* Cross Product of two vectors with first point at origin */
92395 var crossProduct$1 = function crossProduct(a, b) {
92396 return a.x * b.y - a.y * b.x;
92398 /* Dot Product of two vectors with first point at origin */
92401 var dotProduct$1 = function dotProduct(a, b) {
92402 return a.x * b.x + a.y * b.y;
92404 /* Comparator for two vectors with same starting point */
92407 var compareVectorAngles = function compareVectorAngles(basePt, endPt1, endPt2) {
92409 x: endPt1.x - basePt.x,
92410 y: endPt1.y - basePt.y
92413 x: endPt2.x - basePt.x,
92414 y: endPt2.y - basePt.y
92416 var kross = crossProduct$1(v1, v2);
92417 return cmp(kross, 0);
92420 var length = function length(v) {
92421 return Math.sqrt(dotProduct$1(v, v));
92423 /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */
92426 var sineOfAngle = function sineOfAngle(pShared, pBase, pAngle) {
92428 x: pBase.x - pShared.x,
92429 y: pBase.y - pShared.y
92432 x: pAngle.x - pShared.x,
92433 y: pAngle.y - pShared.y
92435 return crossProduct$1(vAngle, vBase) / length(vAngle) / length(vBase);
92437 /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */
92440 var cosineOfAngle = function cosineOfAngle(pShared, pBase, pAngle) {
92442 x: pBase.x - pShared.x,
92443 y: pBase.y - pShared.y
92446 x: pAngle.x - pShared.x,
92447 y: pAngle.y - pShared.y
92449 return dotProduct$1(vAngle, vBase) / length(vAngle) / length(vBase);
92451 /* Get the x coordinate where the given line (defined by a point and vector)
92452 * crosses the horizontal line with the given y coordiante.
92453 * In the case of parrallel lines (including overlapping ones) returns null. */
92456 var horizontalIntersection = function horizontalIntersection(pt, v, y) {
92457 if (v.y === 0) return null;
92459 x: pt.x + v.x / v.y * (y - pt.y),
92463 /* Get the y coordinate where the given line (defined by a point and vector)
92464 * crosses the vertical line with the given x coordiante.
92465 * In the case of parrallel lines (including overlapping ones) returns null. */
92468 var verticalIntersection = function verticalIntersection(pt, v, x) {
92469 if (v.x === 0) return null;
92472 y: pt.y + v.y / v.x * (x - pt.x)
92475 /* Get the intersection of two lines, each defined by a base point and a vector.
92476 * In the case of parrallel lines (including overlapping ones) returns null. */
92479 var intersection$1 = function intersection(pt1, v1, pt2, v2) {
92480 // take some shortcuts for vertical and horizontal lines
92481 // this also ensures we don't calculate an intersection and then discover
92482 // it's actually outside the bounding box of the line
92483 if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
92484 if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
92485 if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
92486 if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y); // General case for non-overlapping segments.
92487 // This algorithm is based on Schneider and Eberly.
92488 // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244
92490 var kross = crossProduct$1(v1, v2);
92491 if (kross == 0) return null;
92496 var d1 = crossProduct$1(ve, v1) / kross;
92497 var d2 = crossProduct$1(ve, v2) / kross; // take the average of the two calculations to minimize rounding error
92499 var x1 = pt1.x + d2 * v1.x,
92500 x2 = pt2.x + d1 * v2.x;
92501 var y1 = pt1.y + d2 * v1.y,
92502 y2 = pt2.y + d1 * v2.y;
92503 var x = (x1 + x2) / 2;
92504 var y = (y1 + y2) / 2;
92511 var SweepEvent$1 = /*#__PURE__*/function () {
92512 _createClass$1(SweepEvent, null, [{
92514 // for ordering sweep events in the sweep event queue
92515 value: function compare(a, b) {
92516 // favor event with a point that the sweep line hits first
92517 var ptCmp = SweepEvent.comparePoints(a.point, b.point);
92518 if (ptCmp !== 0) return ptCmp; // the points are the same, so link them if needed
92520 if (a.point !== b.point) a.link(b); // favor right events over left
92522 if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1; // we have two matching left or right endpoints
92523 // ordering of this case is the same as for their segments
92525 return Segment.compare(a.segment, b.segment);
92526 } // for ordering points in sweep line order
92529 key: "comparePoints",
92530 value: function comparePoints(aPt, bPt) {
92531 if (aPt.x < bPt.x) return -1;
92532 if (aPt.x > bPt.x) return 1;
92533 if (aPt.y < bPt.y) return -1;
92534 if (aPt.y > bPt.y) return 1;
92536 } // Warning: 'point' input will be modified and re-used (for performance)
92540 function SweepEvent(point, isLeft) {
92541 _classCallCheck$1(this, SweepEvent);
92543 if (point.events === undefined) point.events = [this];else point.events.push(this);
92544 this.point = point;
92545 this.isLeft = isLeft; // this.segment, this.otherSE set by factory
92548 _createClass$1(SweepEvent, [{
92550 value: function link(other) {
92551 if (other.point === this.point) {
92552 throw new Error('Tried to link already linked events');
92555 var otherEvents = other.point.events;
92557 for (var i = 0, iMax = otherEvents.length; i < iMax; i++) {
92558 var evt = otherEvents[i];
92559 this.point.events.push(evt);
92560 evt.point = this.point;
92563 this.checkForConsuming();
92565 /* Do a pass over our linked events and check to see if any pair
92566 * of segments match, and should be consumed. */
92569 key: "checkForConsuming",
92570 value: function checkForConsuming() {
92571 // FIXME: The loops in this method run O(n^2) => no good.
92572 // Maintain little ordered sweep event trees?
92573 // Can we maintaining an ordering that avoids the need
92574 // for the re-sorting with getLeftmostComparator in geom-out?
92575 // Compare each pair of events to see if other events also match
92576 var numEvents = this.point.events.length;
92578 for (var i = 0; i < numEvents; i++) {
92579 var evt1 = this.point.events[i];
92580 if (evt1.segment.consumedBy !== undefined) continue;
92582 for (var j = i + 1; j < numEvents; j++) {
92583 var evt2 = this.point.events[j];
92584 if (evt2.consumedBy !== undefined) continue;
92585 if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
92586 evt1.segment.consume(evt2.segment);
92591 key: "getAvailableLinkedEvents",
92592 value: function getAvailableLinkedEvents() {
92593 // point.events is always of length 2 or greater
92596 for (var i = 0, iMax = this.point.events.length; i < iMax; i++) {
92597 var evt = this.point.events[i];
92599 if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
92607 * Returns a comparator function for sorting linked events that will
92608 * favor the event that will give us the smallest left-side angle.
92609 * All ring construction starts as low as possible heading to the right,
92610 * so by always turning left as sharp as possible we'll get polygons
92611 * without uncessary loops & holes.
92613 * The comparator function has a compute cache such that it avoids
92614 * re-computing already-computed values.
92618 key: "getLeftmostComparator",
92619 value: function getLeftmostComparator(baseEvent) {
92622 var cache = new Map();
92624 var fillCache = function fillCache(linkedEvent) {
92625 var nextEvent = linkedEvent.otherSE;
92626 cache.set(linkedEvent, {
92627 sine: sineOfAngle(_this.point, baseEvent.point, nextEvent.point),
92628 cosine: cosineOfAngle(_this.point, baseEvent.point, nextEvent.point)
92632 return function (a, b) {
92633 if (!cache.has(a)) fillCache(a);
92634 if (!cache.has(b)) fillCache(b);
92636 var _cache$get = cache.get(a),
92637 asine = _cache$get.sine,
92638 acosine = _cache$get.cosine;
92640 var _cache$get2 = cache.get(b),
92641 bsine = _cache$get2.sine,
92642 bcosine = _cache$get2.cosine; // both on or above x-axis
92645 if (asine >= 0 && bsine >= 0) {
92646 if (acosine < bcosine) return 1;
92647 if (acosine > bcosine) return -1;
92649 } // both below x-axis
92652 if (asine < 0 && bsine < 0) {
92653 if (acosine < bcosine) return -1;
92654 if (acosine > bcosine) return 1;
92656 } // one above x-axis, one below
92659 if (bsine < asine) return -1;
92660 if (bsine > asine) return 1;
92667 }(); // segments and sweep events when all else is identical
92672 var Segment = /*#__PURE__*/function () {
92673 _createClass$1(Segment, null, [{
92676 /* This compare() function is for ordering segments in the sweep
92677 * line tree, and does so according to the following criteria:
92679 * Consider the vertical line that lies an infinestimal step to the
92680 * right of the right-more of the two left endpoints of the input
92681 * segments. Imagine slowly moving a point up from negative infinity
92682 * in the increasing y direction. Which of the two segments will that
92683 * point intersect first? That segment comes 'before' the other one.
92685 * If neither segment would be intersected by such a line, (if one
92686 * or more of the segments are vertical) then the line to be considered
92687 * is directly on the right-more of the two left inputs.
92689 value: function compare(a, b) {
92690 var alx = a.leftSE.point.x;
92691 var blx = b.leftSE.point.x;
92692 var arx = a.rightSE.point.x;
92693 var brx = b.rightSE.point.x; // check if they're even in the same vertical plane
92695 if (brx < alx) return 1;
92696 if (arx < blx) return -1;
92697 var aly = a.leftSE.point.y;
92698 var bly = b.leftSE.point.y;
92699 var ary = a.rightSE.point.y;
92700 var bry = b.rightSE.point.y; // is left endpoint of segment B the right-more?
92703 // are the two segments in the same horizontal plane?
92704 if (bly < aly && bly < ary) return 1;
92705 if (bly > aly && bly > ary) return -1; // is the B left endpoint colinear to segment A?
92707 var aCmpBLeft = a.comparePoint(b.leftSE.point);
92708 if (aCmpBLeft < 0) return 1;
92709 if (aCmpBLeft > 0) return -1; // is the A right endpoint colinear to segment B ?
92711 var bCmpARight = b.comparePoint(a.rightSE.point);
92712 if (bCmpARight !== 0) return bCmpARight; // colinear segments, consider the one with left-more
92713 // left endpoint to be first (arbitrary?)
92716 } // is left endpoint of segment A the right-more?
92720 if (aly < bly && aly < bry) return -1;
92721 if (aly > bly && aly > bry) return 1; // is the A left endpoint colinear to segment B?
92723 var bCmpALeft = b.comparePoint(a.leftSE.point);
92724 if (bCmpALeft !== 0) return bCmpALeft; // is the B right endpoint colinear to segment A?
92726 var aCmpBRight = a.comparePoint(b.rightSE.point);
92727 if (aCmpBRight < 0) return 1;
92728 if (aCmpBRight > 0) return -1; // colinear segments, consider the one with left-more
92729 // left endpoint to be first (arbitrary?)
92732 } // if we get here, the two left endpoints are in the same
92733 // vertical plane, ie alx === blx
92734 // consider the lower left-endpoint to come first
92737 if (aly < bly) return -1;
92738 if (aly > bly) return 1; // left endpoints are identical
92739 // check for colinearity by using the left-more right endpoint
92740 // is the A right endpoint more left-more?
92743 var _bCmpARight = b.comparePoint(a.rightSE.point);
92745 if (_bCmpARight !== 0) return _bCmpARight;
92746 } // is the B right endpoint more left-more?
92750 var _aCmpBRight = a.comparePoint(b.rightSE.point);
92752 if (_aCmpBRight < 0) return 1;
92753 if (_aCmpBRight > 0) return -1;
92757 // are these two [almost] vertical segments with opposite orientation?
92758 // if so, the one with the lower right endpoint comes first
92759 var ay = ary - aly;
92760 var ax = arx - alx;
92761 var by = bry - bly;
92762 var bx = brx - blx;
92763 if (ay > ax && by < bx) return 1;
92764 if (ay < ax && by > bx) return -1;
92765 } // we have colinear segments with matching orientation
92766 // consider the one with more left-more right endpoint to be first
92769 if (arx > brx) return 1;
92770 if (arx < brx) return -1; // if we get here, two two right endpoints are in the same
92771 // vertical plane, ie arx === brx
92772 // consider the lower right-endpoint to come first
92774 if (ary < bry) return -1;
92775 if (ary > bry) return 1; // right endpoints identical as well, so the segments are idential
92776 // fall back on creation order as consistent tie-breaker
92778 if (a.id < b.id) return -1;
92779 if (a.id > b.id) return 1; // identical segment, ie a === b
92783 /* Warning: a reference to ringWindings input will be stored,
92784 * and possibly will be later modified */
92788 function Segment(leftSE, rightSE, rings, windings) {
92789 _classCallCheck$1(this, Segment);
92791 this.id = ++segmentId;
92792 this.leftSE = leftSE;
92793 leftSE.segment = this;
92794 leftSE.otherSE = rightSE;
92795 this.rightSE = rightSE;
92796 rightSE.segment = this;
92797 rightSE.otherSE = leftSE;
92798 this.rings = rings;
92799 this.windings = windings; // left unset for performance, set later in algorithm
92800 // this.ringOut, this.consumedBy, this.prev
92803 _createClass$1(Segment, [{
92804 key: "replaceRightSE",
92806 /* When a segment is split, the rightSE is replaced with a new sweep event */
92807 value: function replaceRightSE(newRightSE) {
92808 this.rightSE = newRightSE;
92809 this.rightSE.segment = this;
92810 this.rightSE.otherSE = this.leftSE;
92811 this.leftSE.otherSE = this.rightSE;
92815 value: function bbox() {
92816 var y1 = this.leftSE.point.y;
92817 var y2 = this.rightSE.point.y;
92820 x: this.leftSE.point.x,
92821 y: y1 < y2 ? y1 : y2
92824 x: this.rightSE.point.x,
92825 y: y1 > y2 ? y1 : y2
92829 /* A vector from the left point to the right */
92833 value: function vector() {
92835 x: this.rightSE.point.x - this.leftSE.point.x,
92836 y: this.rightSE.point.y - this.leftSE.point.y
92840 key: "isAnEndpoint",
92841 value: function isAnEndpoint(pt) {
92842 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;
92844 /* Compare this segment with a point.
92846 * A point P is considered to be colinear to a segment if there
92847 * exists a distance D such that if we travel along the segment
92848 * from one * endpoint towards the other a distance D, we find
92849 * ourselves at point P.
92851 * Return value indicates:
92853 * 1: point lies above the segment (to the left of vertical)
92854 * 0: point is colinear to segment
92855 * -1: point lies below the segment (to the right of vertical)
92859 key: "comparePoint",
92860 value: function comparePoint(point) {
92861 if (this.isAnEndpoint(point)) return 0;
92862 var lPt = this.leftSE.point;
92863 var rPt = this.rightSE.point;
92864 var v = this.vector(); // Exactly vertical segments.
92866 if (lPt.x === rPt.x) {
92867 if (point.x === lPt.x) return 0;
92868 return point.x < lPt.x ? 1 : -1;
92869 } // Nearly vertical segments with an intersection.
92870 // Check to see where a point on the line with matching Y coordinate is.
92873 var yDist = (point.y - lPt.y) / v.y;
92874 var xFromYDist = lPt.x + yDist * v.x;
92875 if (point.x === xFromYDist) return 0; // General case.
92876 // Check to see where a point on the line with matching X coordinate is.
92878 var xDist = (point.x - lPt.x) / v.x;
92879 var yFromXDist = lPt.y + xDist * v.y;
92880 if (point.y === yFromXDist) return 0;
92881 return point.y < yFromXDist ? -1 : 1;
92884 * Given another segment, returns the first non-trivial intersection
92885 * between the two segments (in terms of sweep line ordering), if it exists.
92887 * A 'non-trivial' intersection is one that will cause one or both of the
92888 * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
92890 * * endpoint of segA with endpoint of segB --> trivial
92891 * * endpoint of segA with point along segB --> non-trivial
92892 * * endpoint of segB with point along segA --> non-trivial
92893 * * point along segA with point along segB --> non-trivial
92895 * If no non-trivial intersection exists, return null
92896 * Else, return null.
92900 key: "getIntersection",
92901 value: function getIntersection(other) {
92902 // If bboxes don't overlap, there can't be any intersections
92903 var tBbox = this.bbox();
92904 var oBbox = other.bbox();
92905 var bboxOverlap = getBboxOverlap(tBbox, oBbox);
92906 if (bboxOverlap === null) return null; // We first check to see if the endpoints can be considered intersections.
92907 // This will 'snap' intersections to endpoints if possible, and will
92908 // handle cases of colinearity.
92910 var tlp = this.leftSE.point;
92911 var trp = this.rightSE.point;
92912 var olp = other.leftSE.point;
92913 var orp = other.rightSE.point; // does each endpoint touch the other segment?
92914 // note that we restrict the 'touching' definition to only allow segments
92915 // to touch endpoints that lie forward from where we are in the sweep line pass
92917 var touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
92918 var touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
92919 var touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
92920 var touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0; // do left endpoints match?
92922 if (touchesThisLSE && touchesOtherLSE) {
92923 // these two cases are for colinear segments with matching left
92924 // endpoints, and one segment being longer than the other
92925 if (touchesThisRSE && !touchesOtherRSE) return trp;
92926 if (!touchesThisRSE && touchesOtherRSE) return orp; // either the two segments match exactly (two trival intersections)
92927 // or just on their left endpoint (one trivial intersection
92930 } // does this left endpoint matches (other doesn't)
92933 if (touchesThisLSE) {
92934 // check for segments that just intersect on opposing endpoints
92935 if (touchesOtherRSE) {
92936 if (tlp.x === orp.x && tlp.y === orp.y) return null;
92937 } // t-intersection on left endpoint
92941 } // does other left endpoint matches (this doesn't)
92944 if (touchesOtherLSE) {
92945 // check for segments that just intersect on opposing endpoints
92946 if (touchesThisRSE) {
92947 if (trp.x === olp.x && trp.y === olp.y) return null;
92948 } // t-intersection on left endpoint
92952 } // trivial intersection on right endpoints
92955 if (touchesThisRSE && touchesOtherRSE) return null; // t-intersections on just one right endpoint
92957 if (touchesThisRSE) return trp;
92958 if (touchesOtherRSE) return orp; // None of our endpoints intersect. Look for a general intersection between
92959 // infinite lines laid over the segments
92961 var pt = intersection$1(tlp, this.vector(), olp, other.vector()); // are the segments parrallel? Note that if they were colinear with overlap,
92962 // they would have an endpoint intersection and that case was already handled above
92964 if (pt === null) return null; // is the intersection found between the lines not on the segments?
92966 if (!isInBbox(bboxOverlap, pt)) return null; // round the the computed point if needed
92968 return rounder.round(pt.x, pt.y);
92971 * Split the given segment into multiple segments on the given points.
92972 * * Each existing segment will retain its leftSE and a new rightSE will be
92973 * generated for it.
92974 * * A new segment will be generated which will adopt the original segment's
92975 * rightSE, and a new leftSE will be generated for it.
92976 * * If there are more than two points given to split on, new segments
92977 * in the middle will be generated with new leftSE and rightSE's.
92978 * * An array of the newly generated SweepEvents will be returned.
92980 * Warning: input array of points is modified
92985 value: function split(point) {
92986 var newEvents = [];
92987 var alreadyLinked = point.events !== undefined;
92988 var newLeftSE = new SweepEvent$1(point, true);
92989 var newRightSE = new SweepEvent$1(point, false);
92990 var oldRightSE = this.rightSE;
92991 this.replaceRightSE(newRightSE);
92992 newEvents.push(newRightSE);
92993 newEvents.push(newLeftSE);
92994 var newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice()); // when splitting a nearly vertical downward-facing segment,
92995 // sometimes one of the resulting new segments is vertical, in which
92996 // case its left and right events may need to be swapped
92998 if (SweepEvent$1.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
92999 newSeg.swapEvents();
93002 if (SweepEvent$1.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
93004 } // in the point we just used to create new sweep events with was already
93005 // linked to other events, we need to check if either of the affected
93006 // segments should be consumed
93009 if (alreadyLinked) {
93010 newLeftSE.checkForConsuming();
93011 newRightSE.checkForConsuming();
93016 /* Swap which event is left and right */
93020 value: function swapEvents() {
93021 var tmpEvt = this.rightSE;
93022 this.rightSE = this.leftSE;
93023 this.leftSE = tmpEvt;
93024 this.leftSE.isLeft = true;
93025 this.rightSE.isLeft = false;
93027 for (var i = 0, iMax = this.windings.length; i < iMax; i++) {
93028 this.windings[i] *= -1;
93031 /* Consume another segment. We take their rings under our wing
93032 * and mark them as consumed. Use for perfectly overlapping segments */
93036 value: function consume(other) {
93037 var consumer = this;
93038 var consumee = other;
93040 while (consumer.consumedBy) {
93041 consumer = consumer.consumedBy;
93044 while (consumee.consumedBy) {
93045 consumee = consumee.consumedBy;
93048 var cmp = Segment.compare(consumer, consumee);
93049 if (cmp === 0) return; // already consumed
93050 // the winner of the consumption is the earlier segment
93051 // according to sweep line ordering
93054 var tmp = consumer;
93055 consumer = consumee;
93057 } // make sure a segment doesn't consume it's prev
93060 if (consumer.prev === consumee) {
93061 var _tmp = consumer;
93062 consumer = consumee;
93066 for (var i = 0, iMax = consumee.rings.length; i < iMax; i++) {
93067 var ring = consumee.rings[i];
93068 var winding = consumee.windings[i];
93069 var index = consumer.rings.indexOf(ring);
93071 if (index === -1) {
93072 consumer.rings.push(ring);
93073 consumer.windings.push(winding);
93074 } else consumer.windings[index] += winding;
93077 consumee.rings = null;
93078 consumee.windings = null;
93079 consumee.consumedBy = consumer; // mark sweep events consumed as to maintain ordering in sweep event queue
93081 consumee.leftSE.consumedBy = consumer.leftSE;
93082 consumee.rightSE.consumedBy = consumer.rightSE;
93084 /* The first segment previous segment chain that is in the result */
93087 key: "prevInResult",
93088 value: function prevInResult() {
93089 if (this._prevInResult !== undefined) return this._prevInResult;
93090 if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult();
93091 return this._prevInResult;
93094 key: "beforeState",
93095 value: function beforeState() {
93096 if (this._beforeState !== undefined) return this._beforeState;
93097 if (!this.prev) this._beforeState = {
93102 var seg = this.prev.consumedBy || this.prev;
93103 this._beforeState = seg.afterState();
93105 return this._beforeState;
93109 value: function afterState() {
93110 if (this._afterState !== undefined) return this._afterState;
93111 var beforeState = this.beforeState();
93112 this._afterState = {
93113 rings: beforeState.rings.slice(0),
93114 windings: beforeState.windings.slice(0),
93117 var ringsAfter = this._afterState.rings;
93118 var windingsAfter = this._afterState.windings;
93119 var mpsAfter = this._afterState.multiPolys; // calculate ringsAfter, windingsAfter
93121 for (var i = 0, iMax = this.rings.length; i < iMax; i++) {
93122 var ring = this.rings[i];
93123 var winding = this.windings[i];
93124 var index = ringsAfter.indexOf(ring);
93126 if (index === -1) {
93127 ringsAfter.push(ring);
93128 windingsAfter.push(winding);
93129 } else windingsAfter[index] += winding;
93130 } // calcualte polysAfter
93133 var polysAfter = [];
93134 var polysExclude = [];
93136 for (var _i = 0, _iMax = ringsAfter.length; _i < _iMax; _i++) {
93137 if (windingsAfter[_i] === 0) continue; // non-zero rule
93139 var _ring = ringsAfter[_i];
93140 var poly = _ring.poly;
93141 if (polysExclude.indexOf(poly) !== -1) continue;
93142 if (_ring.isExterior) polysAfter.push(poly);else {
93143 if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
93145 var _index = polysAfter.indexOf(_ring.poly);
93147 if (_index !== -1) polysAfter.splice(_index, 1);
93149 } // calculate multiPolysAfter
93152 for (var _i2 = 0, _iMax2 = polysAfter.length; _i2 < _iMax2; _i2++) {
93153 var mp = polysAfter[_i2].multiPoly;
93154 if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
93157 return this._afterState;
93159 /* Is this segment part of the final result? */
93163 value: function isInResult() {
93164 // if we've been consumed, we're not in the result
93165 if (this.consumedBy) return false;
93166 if (this._isInResult !== undefined) return this._isInResult;
93167 var mpsBefore = this.beforeState().multiPolys;
93168 var mpsAfter = this.afterState().multiPolys;
93170 switch (operation.type) {
93173 // UNION - included iff:
93174 // * On one side of us there is 0 poly interiors AND
93175 // * On the other side there is 1 or more.
93176 var noBefores = mpsBefore.length === 0;
93177 var noAfters = mpsAfter.length === 0;
93178 this._isInResult = noBefores !== noAfters;
93182 case 'intersection':
93184 // INTERSECTION - included iff:
93185 // * on one side of us all multipolys are rep. with poly interiors AND
93186 // * on the other side of us, not all multipolys are repsented
93187 // with poly interiors
93191 if (mpsBefore.length < mpsAfter.length) {
93192 least = mpsBefore.length;
93193 most = mpsAfter.length;
93195 least = mpsAfter.length;
93196 most = mpsBefore.length;
93199 this._isInResult = most === operation.numMultiPolys && least < most;
93205 // XOR - included iff:
93206 // * the difference between the number of multipolys represented
93207 // with poly interiors on our two sides is an odd number
93208 var diff = Math.abs(mpsBefore.length - mpsAfter.length);
93209 this._isInResult = diff % 2 === 1;
93215 // DIFFERENCE included iff:
93216 // * on exactly one side, we have just the subject
93217 var isJustSubject = function isJustSubject(mps) {
93218 return mps.length === 1 && mps[0].isSubject;
93221 this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
93226 throw new Error("Unrecognized operation type found ".concat(operation.type));
93229 return this._isInResult;
93233 value: function fromRing(pt1, pt2, ring) {
93234 var leftPt, rightPt, winding; // ordering the two points according to sweep line ordering
93236 var cmpPts = SweepEvent$1.comparePoints(pt1, pt2);
93242 } else if (cmpPts > 0) {
93246 } else throw new Error("Tried to create degenerate segment at [".concat(pt1.x, ", ").concat(pt1.y, "]"));
93248 var leftSE = new SweepEvent$1(leftPt, true);
93249 var rightSE = new SweepEvent$1(rightPt, false);
93250 return new Segment(leftSE, rightSE, [ring], [winding]);
93257 var RingIn = /*#__PURE__*/function () {
93258 function RingIn(geomRing, poly, isExterior) {
93259 _classCallCheck$1(this, RingIn);
93261 if (!Array.isArray(geomRing) || geomRing.length === 0) {
93262 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93266 this.isExterior = isExterior;
93267 this.segments = [];
93269 if (typeof geomRing[0][0] !== 'number' || typeof geomRing[0][1] !== 'number') {
93270 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93273 var firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
93284 var prevPoint = firstPoint;
93286 for (var i = 1, iMax = geomRing.length; i < iMax; i++) {
93287 if (typeof geomRing[i][0] !== 'number' || typeof geomRing[i][1] !== 'number') {
93288 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93291 var point = rounder.round(geomRing[i][0], geomRing[i][1]); // skip repeated points
93293 if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
93294 this.segments.push(Segment.fromRing(prevPoint, point, this));
93295 if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
93296 if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
93297 if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
93298 if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
93300 } // add segment from last to first if last is not the same as first
93303 if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
93304 this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
93308 _createClass$1(RingIn, [{
93309 key: "getSweepEvents",
93310 value: function getSweepEvents() {
93311 var sweepEvents = [];
93313 for (var i = 0, iMax = this.segments.length; i < iMax; i++) {
93314 var segment = this.segments[i];
93315 sweepEvents.push(segment.leftSE);
93316 sweepEvents.push(segment.rightSE);
93319 return sweepEvents;
93326 var PolyIn = /*#__PURE__*/function () {
93327 function PolyIn(geomPoly, multiPoly) {
93328 _classCallCheck$1(this, PolyIn);
93330 if (!Array.isArray(geomPoly)) {
93331 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93334 this.exteriorRing = new RingIn(geomPoly[0], this, true); // copy by value
93338 x: this.exteriorRing.bbox.ll.x,
93339 y: this.exteriorRing.bbox.ll.y
93342 x: this.exteriorRing.bbox.ur.x,
93343 y: this.exteriorRing.bbox.ur.y
93346 this.interiorRings = [];
93348 for (var i = 1, iMax = geomPoly.length; i < iMax; i++) {
93349 var ring = new RingIn(geomPoly[i], this, false);
93350 if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
93351 if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
93352 if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
93353 if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
93354 this.interiorRings.push(ring);
93357 this.multiPoly = multiPoly;
93360 _createClass$1(PolyIn, [{
93361 key: "getSweepEvents",
93362 value: function getSweepEvents() {
93363 var sweepEvents = this.exteriorRing.getSweepEvents();
93365 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
93366 var ringSweepEvents = this.interiorRings[i].getSweepEvents();
93368 for (var j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
93369 sweepEvents.push(ringSweepEvents[j]);
93373 return sweepEvents;
93380 var MultiPolyIn = /*#__PURE__*/function () {
93381 function MultiPolyIn(geom, isSubject) {
93382 _classCallCheck$1(this, MultiPolyIn);
93384 if (!Array.isArray(geom)) {
93385 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93389 // if the input looks like a polygon, convert it to a multipolygon
93390 if (typeof geom[0][0][0] === 'number') geom = [geom];
93391 } catch (ex) {// The input is either malformed or has empty arrays.
93392 // In either case, it will be handled later on.
93398 x: Number.POSITIVE_INFINITY,
93399 y: Number.POSITIVE_INFINITY
93402 x: Number.NEGATIVE_INFINITY,
93403 y: Number.NEGATIVE_INFINITY
93407 for (var i = 0, iMax = geom.length; i < iMax; i++) {
93408 var poly = new PolyIn(geom[i], this);
93409 if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
93410 if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
93411 if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
93412 if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
93413 this.polys.push(poly);
93416 this.isSubject = isSubject;
93419 _createClass$1(MultiPolyIn, [{
93420 key: "getSweepEvents",
93421 value: function getSweepEvents() {
93422 var sweepEvents = [];
93424 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
93425 var polySweepEvents = this.polys[i].getSweepEvents();
93427 for (var j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
93428 sweepEvents.push(polySweepEvents[j]);
93432 return sweepEvents;
93436 return MultiPolyIn;
93439 var RingOut = /*#__PURE__*/function () {
93440 _createClass$1(RingOut, null, [{
93443 /* Given the segments from the sweep line pass, compute & return a series
93444 * of closed rings from all the segments marked to be part of the result */
93445 value: function factory(allSegments) {
93448 for (var i = 0, iMax = allSegments.length; i < iMax; i++) {
93449 var segment = allSegments[i];
93450 if (!segment.isInResult() || segment.ringOut) continue;
93451 var prevEvent = null;
93452 var event = segment.leftSE;
93453 var nextEvent = segment.rightSE;
93454 var events = [event];
93455 var startingPoint = event.point;
93456 var intersectionLEs = [];
93457 /* Walk the chain of linked events to form a closed ring */
93462 events.push(event);
93463 /* Is the ring complete? */
93465 if (event.point === startingPoint) break;
93468 var availableLEs = event.getAvailableLinkedEvents();
93469 /* Did we hit a dead end? This shouldn't happen. Indicates some earlier
93470 * part of the algorithm malfunctioned... please file a bug report. */
93472 if (availableLEs.length === 0) {
93473 var firstPt = events[0].point;
93474 var lastPt = events[events.length - 1].point;
93475 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, "]."));
93477 /* Only one way to go, so cotinue on the path */
93480 if (availableLEs.length === 1) {
93481 nextEvent = availableLEs[0].otherSE;
93484 /* We must have an intersection. Check for a completed loop */
93487 var indexLE = null;
93489 for (var j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
93490 if (intersectionLEs[j].point === event.point) {
93495 /* Found a completed loop. Cut that off and make a ring */
93498 if (indexLE !== null) {
93499 var intersectionLE = intersectionLEs.splice(indexLE)[0];
93500 var ringEvents = events.splice(intersectionLE.index);
93501 ringEvents.unshift(ringEvents[0].otherSE);
93502 ringsOut.push(new RingOut(ringEvents.reverse()));
93505 /* register the intersection */
93508 intersectionLEs.push({
93509 index: events.length,
93512 /* Choose the left-most option to continue the walk */
93514 var comparator = event.getLeftmostComparator(prevEvent);
93515 nextEvent = availableLEs.sort(comparator)[0].otherSE;
93520 ringsOut.push(new RingOut(events));
93527 function RingOut(events) {
93528 _classCallCheck$1(this, RingOut);
93530 this.events = events;
93532 for (var i = 0, iMax = events.length; i < iMax; i++) {
93533 events[i].segment.ringOut = this;
93539 _createClass$1(RingOut, [{
93541 value: function getGeom() {
93542 // Remove superfluous points (ie extra points along a straight line),
93543 var prevPt = this.events[0].point;
93544 var points = [prevPt];
93546 for (var i = 1, iMax = this.events.length - 1; i < iMax; i++) {
93547 var _pt = this.events[i].point;
93548 var _nextPt = this.events[i + 1].point;
93549 if (compareVectorAngles(_pt, prevPt, _nextPt) === 0) continue;
93552 } // ring was all (within rounding error of angle calc) colinear points
93555 if (points.length === 1) return null; // check if the starting point is necessary
93557 var pt = points[0];
93558 var nextPt = points[1];
93559 if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
93560 points.push(points[0]);
93561 var step = this.isExteriorRing() ? 1 : -1;
93562 var iStart = this.isExteriorRing() ? 0 : points.length - 1;
93563 var iEnd = this.isExteriorRing() ? points.length : -1;
93564 var orderedPoints = [];
93566 for (var _i = iStart; _i != iEnd; _i += step) {
93567 orderedPoints.push([points[_i].x, points[_i].y]);
93570 return orderedPoints;
93573 key: "isExteriorRing",
93574 value: function isExteriorRing() {
93575 if (this._isExteriorRing === undefined) {
93576 var enclosing = this.enclosingRing();
93577 this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
93580 return this._isExteriorRing;
93583 key: "enclosingRing",
93584 value: function enclosingRing() {
93585 if (this._enclosingRing === undefined) {
93586 this._enclosingRing = this._calcEnclosingRing();
93589 return this._enclosingRing;
93591 /* Returns the ring that encloses this one, if any */
93594 key: "_calcEnclosingRing",
93595 value: function _calcEnclosingRing() {
93596 // start with the ealier sweep line event so that the prevSeg
93597 // chain doesn't lead us inside of a loop of ours
93598 var leftMostEvt = this.events[0];
93600 for (var i = 1, iMax = this.events.length; i < iMax; i++) {
93601 var evt = this.events[i];
93602 if (SweepEvent$1.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
93605 var prevSeg = leftMostEvt.segment.prevInResult();
93606 var prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
93609 // no segment found, thus no ring can enclose us
93610 if (!prevSeg) return null; // no segments below prev segment found, thus the ring of the prev
93611 // segment must loop back around and enclose us
93613 if (!prevPrevSeg) return prevSeg.ringOut; // if the two segments are of different rings, the ring of the prev
93614 // segment must either loop around us or the ring of the prev prev
93615 // seg, which would make us and the ring of the prev peers
93617 if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
93618 if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) {
93619 return prevSeg.ringOut;
93620 } else return prevSeg.ringOut.enclosingRing();
93621 } // two segments are from the same ring, so this was a penisula
93622 // of that ring. iterate downward, keep searching
93625 prevSeg = prevPrevSeg.prevInResult();
93626 prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
93634 var PolyOut = /*#__PURE__*/function () {
93635 function PolyOut(exteriorRing) {
93636 _classCallCheck$1(this, PolyOut);
93638 this.exteriorRing = exteriorRing;
93639 exteriorRing.poly = this;
93640 this.interiorRings = [];
93643 _createClass$1(PolyOut, [{
93644 key: "addInterior",
93645 value: function addInterior(ring) {
93646 this.interiorRings.push(ring);
93651 value: function getGeom() {
93652 var geom = [this.exteriorRing.getGeom()]; // exterior ring was all (within rounding error of angle calc) colinear points
93654 if (geom[0] === null) return null;
93656 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
93657 var ringGeom = this.interiorRings[i].getGeom(); // interior ring was all (within rounding error of angle calc) colinear points
93659 if (ringGeom === null) continue;
93660 geom.push(ringGeom);
93670 var MultiPolyOut = /*#__PURE__*/function () {
93671 function MultiPolyOut(rings) {
93672 _classCallCheck$1(this, MultiPolyOut);
93674 this.rings = rings;
93675 this.polys = this._composePolys(rings);
93678 _createClass$1(MultiPolyOut, [{
93680 value: function getGeom() {
93683 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
93684 var polyGeom = this.polys[i].getGeom(); // exterior ring was all (within rounding error of angle calc) colinear points
93686 if (polyGeom === null) continue;
93687 geom.push(polyGeom);
93693 key: "_composePolys",
93694 value: function _composePolys(rings) {
93697 for (var i = 0, iMax = rings.length; i < iMax; i++) {
93698 var ring = rings[i];
93699 if (ring.poly) continue;
93700 if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else {
93701 var enclosingRing = ring.enclosingRing();
93702 if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
93703 enclosingRing.poly.addInterior(ring);
93711 return MultiPolyOut;
93714 * NOTE: We must be careful not to change any segments while
93715 * they are in the SplayTree. AFAIK, there's no way to tell
93716 * the tree to rebalance itself - thus before splitting
93717 * a segment that's in the tree, we remove it from the tree,
93718 * do the split, then re-insert it. (Even though splitting a
93719 * segment *shouldn't* change its correct position in the
93720 * sweep line tree, the reality is because of rounding errors,
93721 * it sometimes does.)
93725 var SweepLine = /*#__PURE__*/function () {
93726 function SweepLine(queue) {
93727 var comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare;
93729 _classCallCheck$1(this, SweepLine);
93731 this.queue = queue;
93732 this.tree = new Tree(comparator);
93733 this.segments = [];
93736 _createClass$1(SweepLine, [{
93738 value: function process(event) {
93739 var segment = event.segment;
93740 var newEvents = []; // if we've already been consumed by another segment,
93741 // clean up our body parts and get out
93743 if (event.consumedBy) {
93744 if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment);
93748 var node = event.isLeft ? this.tree.insert(segment) : this.tree.find(segment);
93749 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.');
93750 var prevNode = node;
93751 var nextNode = node;
93752 var prevSeg = undefined;
93753 var nextSeg = undefined; // skip consumed segments still in tree
93755 while (prevSeg === undefined) {
93756 prevNode = this.tree.prev(prevNode);
93757 if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key;
93758 } // skip consumed segments still in tree
93761 while (nextSeg === undefined) {
93762 nextNode = this.tree.next(nextNode);
93763 if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key;
93766 if (event.isLeft) {
93767 // Check for intersections against the previous segment in the sweep line
93768 var prevMySplitter = null;
93771 var prevInter = prevSeg.getIntersection(segment);
93773 if (prevInter !== null) {
93774 if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
93776 if (!prevSeg.isAnEndpoint(prevInter)) {
93777 var newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
93779 for (var i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
93780 newEvents.push(newEventsFromSplit[i]);
93784 } // Check for intersections against the next segment in the sweep line
93787 var nextMySplitter = null;
93790 var nextInter = nextSeg.getIntersection(segment);
93792 if (nextInter !== null) {
93793 if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
93795 if (!nextSeg.isAnEndpoint(nextInter)) {
93796 var _newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
93798 for (var _i = 0, _iMax = _newEventsFromSplit.length; _i < _iMax; _i++) {
93799 newEvents.push(_newEventsFromSplit[_i]);
93803 } // For simplicity, even if we find more than one intersection we only
93804 // spilt on the 'earliest' (sweep-line style) of the intersections.
93805 // The other intersection will be handled in a future process().
93808 if (prevMySplitter !== null || nextMySplitter !== null) {
93809 var mySplitter = null;
93810 if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else {
93811 var cmpSplitters = SweepEvent$1.comparePoints(prevMySplitter, nextMySplitter);
93812 mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
93813 } // Rounding errors can cause changes in ordering,
93814 // so remove afected segments and right sweep events before splitting
93816 this.queue.remove(segment.rightSE);
93817 newEvents.push(segment.rightSE);
93819 var _newEventsFromSplit2 = segment.split(mySplitter);
93821 for (var _i2 = 0, _iMax2 = _newEventsFromSplit2.length; _i2 < _iMax2; _i2++) {
93822 newEvents.push(_newEventsFromSplit2[_i2]);
93826 if (newEvents.length > 0) {
93827 // We found some intersections, so re-do the current event to
93828 // make sure sweep line ordering is totally consistent for later
93829 // use with the segment 'prev' pointers
93830 this.tree.remove(segment);
93831 newEvents.push(event);
93833 // done with left event
93834 this.segments.push(segment);
93835 segment.prev = prevSeg;
93839 // since we're about to be removed from the sweep line, check for
93840 // intersections between our previous and next segments
93841 if (prevSeg && nextSeg) {
93842 var inter = prevSeg.getIntersection(nextSeg);
93844 if (inter !== null) {
93845 if (!prevSeg.isAnEndpoint(inter)) {
93846 var _newEventsFromSplit3 = this._splitSafely(prevSeg, inter);
93848 for (var _i3 = 0, _iMax3 = _newEventsFromSplit3.length; _i3 < _iMax3; _i3++) {
93849 newEvents.push(_newEventsFromSplit3[_i3]);
93853 if (!nextSeg.isAnEndpoint(inter)) {
93854 var _newEventsFromSplit4 = this._splitSafely(nextSeg, inter);
93856 for (var _i4 = 0, _iMax4 = _newEventsFromSplit4.length; _i4 < _iMax4; _i4++) {
93857 newEvents.push(_newEventsFromSplit4[_i4]);
93863 this.tree.remove(segment);
93868 /* Safely split a segment that is currently in the datastructures
93869 * IE - a segment other than the one that is currently being processed. */
93872 key: "_splitSafely",
93873 value: function _splitSafely(seg, pt) {
93874 // Rounding errors can cause changes in ordering,
93875 // so remove afected segments and right sweep events before splitting
93876 // removeNode() doesn't work, so have re-find the seg
93877 // https://github.com/w8r/splay-tree/pull/5
93878 this.tree.remove(seg);
93879 var rightSE = seg.rightSE;
93880 this.queue.remove(rightSE);
93881 var newEvents = seg.split(pt);
93882 newEvents.push(rightSE); // splitting can trigger consumption
93884 if (seg.consumedBy === undefined) this.tree.insert(seg);
93892 var POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000;
93893 var POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000;
93895 var Operation = /*#__PURE__*/function () {
93896 function Operation() {
93897 _classCallCheck$1(this, Operation);
93900 _createClass$1(Operation, [{
93902 value: function run(type, geom, moreGeoms) {
93903 operation.type = type;
93905 /* Convert inputs to MultiPoly objects */
93907 var multipolys = [new MultiPolyIn(geom, true)];
93909 for (var i = 0, iMax = moreGeoms.length; i < iMax; i++) {
93910 multipolys.push(new MultiPolyIn(moreGeoms[i], false));
93913 operation.numMultiPolys = multipolys.length;
93914 /* BBox optimization for difference operation
93915 * If the bbox of a multipolygon that's part of the clipping doesn't
93916 * intersect the bbox of the subject at all, we can just drop that
93919 if (operation.type === 'difference') {
93920 // in place removal
93921 var subject = multipolys[0];
93924 while (_i < multipolys.length) {
93925 if (getBboxOverlap(multipolys[_i].bbox, subject.bbox) !== null) _i++;else multipolys.splice(_i, 1);
93928 /* BBox optimization for intersection operation
93929 * If we can find any pair of multipolygons whose bbox does not overlap,
93930 * then the result will be empty. */
93933 if (operation.type === 'intersection') {
93934 // TODO: this is O(n^2) in number of polygons. By sorting the bboxes,
93935 // it could be optimized to O(n * ln(n))
93936 for (var _i2 = 0, _iMax = multipolys.length; _i2 < _iMax; _i2++) {
93937 var mpA = multipolys[_i2];
93939 for (var j = _i2 + 1, jMax = multipolys.length; j < jMax; j++) {
93940 if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
93944 /* Put segment endpoints in a priority queue */
93947 var queue = new Tree(SweepEvent$1.compare);
93949 for (var _i3 = 0, _iMax2 = multipolys.length; _i3 < _iMax2; _i3++) {
93950 var sweepEvents = multipolys[_i3].getSweepEvents();
93952 for (var _j = 0, _jMax = sweepEvents.length; _j < _jMax; _j++) {
93953 queue.insert(sweepEvents[_j]);
93955 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
93956 // prevents an infinite loop, an otherwise common manifestation of bugs
93957 throw new Error('Infinite loop when putting segment endpoints in a priority queue ' + '(queue size too big). Please file a bug report.');
93961 /* Pass the sweep line over those endpoints */
93964 var sweepLine = new SweepLine(queue);
93965 var prevQueueSize = queue.size;
93966 var node = queue.pop();
93969 var evt = node.key;
93971 if (queue.size === prevQueueSize) {
93972 // prevents an infinite loop, an otherwise common manifestation of bugs
93973 var seg = evt.segment;
93974 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.');
93977 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
93978 // prevents an infinite loop, an otherwise common manifestation of bugs
93979 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(queue size too big). Please file a bug report.');
93982 if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) {
93983 // prevents an infinite loop, an otherwise common manifestation of bugs
93984 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(too many sweep line segments). Please file a bug report.');
93987 var newEvents = sweepLine.process(evt);
93989 for (var _i4 = 0, _iMax3 = newEvents.length; _i4 < _iMax3; _i4++) {
93990 var _evt = newEvents[_i4];
93991 if (_evt.consumedBy === undefined) queue.insert(_evt);
93994 prevQueueSize = queue.size;
93995 node = queue.pop();
93996 } // free some memory we don't need anymore
94000 /* Collect and compile segments we're keeping into a multipolygon */
94002 var ringsOut = RingOut.factory(sweepLine.segments);
94003 var result = new MultiPolyOut(ringsOut);
94004 return result.getGeom();
94009 }(); // singleton available by import
94012 var operation = new Operation();
94014 var union$1 = function union(geom) {
94015 for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
94016 moreGeoms[_key - 1] = arguments[_key];
94019 return operation.run('union', geom, moreGeoms);
94022 var intersection$1$1 = function intersection(geom) {
94023 for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
94024 moreGeoms[_key2 - 1] = arguments[_key2];
94027 return operation.run('intersection', geom, moreGeoms);
94030 var xor = function xor(geom) {
94031 for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
94032 moreGeoms[_key3 - 1] = arguments[_key3];
94035 return operation.run('xor', geom, moreGeoms);
94038 var difference = function difference(subjectGeom) {
94039 for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
94040 clippingGeoms[_key4 - 1] = arguments[_key4];
94043 return operation.run('difference', subjectGeom, clippingGeoms);
94048 intersection: intersection$1$1,
94050 difference: difference
94053 var geojsonPrecision = createCommonjsModule(function (module) {
94055 function parse(t, coordinatePrecision, extrasPrecision) {
94056 function point(p) {
94057 return p.map(function (e, index) {
94059 return 1 * e.toFixed(coordinatePrecision);
94061 return 1 * e.toFixed(extrasPrecision);
94066 function multi(l) {
94067 return l.map(point);
94071 return p.map(multi);
94074 function multiPoly(m) {
94075 return m.map(poly);
94078 function geometry(obj) {
94083 switch (obj.type) {
94085 obj.coordinates = point(obj.coordinates);
94090 obj.coordinates = multi(obj.coordinates);
94094 case "MultiLineString":
94095 obj.coordinates = poly(obj.coordinates);
94098 case "MultiPolygon":
94099 obj.coordinates = multiPoly(obj.coordinates);
94102 case "GeometryCollection":
94103 obj.geometries = obj.geometries.map(geometry);
94111 function feature(obj) {
94112 obj.geometry = geometry(obj.geometry);
94116 function featureCollection(f) {
94117 f.features = f.features.map(feature);
94121 function geometryCollection(g) {
94122 g.geometries = g.geometries.map(geometry);
94134 case "GeometryCollection":
94135 return geometryCollection(t);
94137 case "FeatureCollection":
94138 return featureCollection(t);
94144 case "MultiPolygon":
94145 case "MultiLineString":
94146 return geometry(t);
94153 module.exports = parse;
94154 module.exports.parse = parse;
94158 function isObject$4(obj) {
94159 return _typeof(obj) === 'object' && obj !== null;
94162 function forEach(obj, cb) {
94163 if (Array.isArray(obj)) {
94165 } else if (isObject$4(obj)) {
94166 Object.keys(obj).forEach(function (key) {
94167 var val = obj[key];
94173 function getTreeDepth(obj) {
94176 if (Array.isArray(obj) || isObject$4(obj)) {
94177 forEach(obj, function (val) {
94178 if (Array.isArray(val) || isObject$4(val)) {
94179 var tmpDepth = getTreeDepth(val);
94181 if (tmpDepth > depth) {
94192 function stringify(obj, options) {
94193 options = options || {};
94194 var indent = JSON.stringify([1], null, get$5(options, 'indent', 2)).slice(2, -3);
94195 var addMargin = get$5(options, 'margins', false);
94196 var addArrayMargin = get$5(options, 'arrayMargins', false);
94197 var addObjectMargin = get$5(options, 'objectMargins', false);
94198 var maxLength = indent === '' ? Infinity : get$5(options, 'maxLength', 80);
94199 var maxNesting = get$5(options, 'maxNesting', Infinity);
94200 return function _stringify(obj, currentIndent, reserved) {
94201 if (obj && typeof obj.toJSON === 'function') {
94202 obj = obj.toJSON();
94205 var string = JSON.stringify(obj);
94207 if (string === undefined) {
94211 var length = maxLength - currentIndent.length - reserved;
94212 var treeDepth = getTreeDepth(obj);
94214 if (treeDepth <= maxNesting && string.length <= length) {
94215 var prettified = prettify(string, {
94216 addMargin: addMargin,
94217 addArrayMargin: addArrayMargin,
94218 addObjectMargin: addObjectMargin
94221 if (prettified.length <= length) {
94226 if (isObject$4(obj)) {
94227 var nextIndent = currentIndent + indent;
94231 var comma = function comma(array, index) {
94232 return index === array.length - 1 ? 0 : 1;
94235 if (Array.isArray(obj)) {
94236 for (var index = 0; index < obj.length; index++) {
94237 items.push(_stringify(obj[index], nextIndent, comma(obj, index)) || 'null');
94242 Object.keys(obj).forEach(function (key, index, array) {
94243 var keyPart = JSON.stringify(key) + ': ';
94245 var value = _stringify(obj[key], nextIndent, keyPart.length + comma(array, index));
94247 if (value !== undefined) {
94248 items.push(keyPart + value);
94254 if (items.length > 0) {
94255 return [delimiters[0], indent + items.join(',\n' + nextIndent), delimiters[1]].join('\n' + currentIndent);
94261 } // Note: This regex matches even invalid JSON strings, but since we’re
94262 // working on the output of `JSON.stringify` we know that only valid strings
94263 // are present (unless the user supplied a weird `options.indent` but in
94264 // that case we don’t care since the output would be invalid anyway).
94267 var stringOrChar = /("(?:[^\\"]|\\.)*")|[:,\][}{]/g;
94269 function prettify(string, options) {
94270 options = options || {};
94280 if (options.addMargin || options.addObjectMargin) {
94281 tokens['{'] = '{ ';
94282 tokens['}'] = ' }';
94285 if (options.addMargin || options.addArrayMargin) {
94286 tokens['['] = '[ ';
94287 tokens[']'] = ' ]';
94290 return string.replace(stringOrChar, function (match, string) {
94291 return string ? match : tokens[match];
94295 function get$5(options, name, defaultValue) {
94296 return name in options ? options[name] : defaultValue;
94299 var jsonStringifyPrettyCompact = stringify;
94301 var _default$3 = /*#__PURE__*/function () {
94304 // `fc` Optional FeatureCollection of known features
94306 // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
94307 // Each feature must have a filename-like `id`, for example: `something.geojson`
94310 // "type": "FeatureCollection"
94313 // "type": "Feature",
94314 // "id": "philly_metro.geojson",
94315 // "properties": { … },
94316 // "geometry": { … }
94320 function _default(fc) {
94323 _classCallCheck(this, _default);
94325 // The _cache retains resolved features, so if you ask for the same thing multiple times
94326 // we don't repeat the expensive resolving/clipping operations.
94328 // Each feature has a stable identifier that is used as the cache key.
94329 // The identifiers look like:
94330 // - for point locations, the stringified point: e.g. '[8.67039,49.41882]'
94331 // - for geojson locations, the geojson id: e.g. 'de-hamburg.geojson'
94332 // - for countrycoder locations, feature.id property: e.g. 'Q2' (countrycoder uses Wikidata identifiers)
94333 // - for aggregated locationSets, +[include]-[exclude]: e.g '+[Q2]-[Q18,Q27611]'
94334 this._cache = {}; // When strict mode = true, throw on invalid locations or locationSets.
94335 // When strict mode = false, return `null` for invalid locations or locationSets.
94337 this._strict = true; // process input FeatureCollection
94339 if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
94340 fc.features.forEach(function (feature) {
94341 feature.properties = feature.properties || {};
94342 var props = feature.properties; // get `id` from either `id` or `properties`
94344 var id = feature.id || props.id;
94345 if (!id || !/^\S+\.geojson$/i.test(id)) return; // ensure `id` exists and is lowercase
94347 id = id.toLowerCase();
94349 props.id = id; // ensure `area` property exists
94352 var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
94354 props.area = Number(area.toFixed(2));
94357 _this._cache[id] = feature;
94359 } // Replace CountryCoder world geometry to be a polygon covering the world.
94362 var world = _cloneDeep(feature('Q2'));
94366 coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
94369 world.properties.id = 'Q2';
94370 world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km²
94372 this._cache.Q2 = world;
94373 } // validateLocation
94374 // `location` The location to validate
94376 // Pass a `location` value to validate
94378 // Returns a result like:
94380 // type: 'point', 'geojson', or 'countrycoder'
94381 // location: the queried location
94382 // id: the stable identifier for the feature
94384 // or `null` if the location is invalid
94388 _createClass(_default, [{
94389 key: "validateLocation",
94390 value: function validateLocation(location) {
94391 if (Array.isArray(location)) {
94392 // a [lon,lat] coordinate pair?
94393 if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) && location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90) {
94394 var id = '[' + location.toString() + ']';
94397 location: location,
94401 } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {
94402 // a .geojson filename?
94403 var _id = location.toLowerCase();
94405 if (this._cache[_id]) {
94408 location: location,
94412 } else if (typeof location === 'string' || typeof location === 'number') {
94413 // a country-coder value?
94414 var feature$1 = feature(location);
94417 // Use wikidata QID as the identifier, since that seems to be the one
94418 // property that everything in CountryCoder is guaranteed to have.
94419 var _id2 = feature$1.properties.wikidata;
94421 type: 'countrycoder',
94422 location: location,
94428 if (this._strict) {
94429 throw new Error("validateLocation: Invalid location: \"".concat(location, "\"."));
94433 } // resolveLocation
94434 // `location` The location to resolve
94436 // Pass a `location` value to resolve
94438 // Returns a result like:
94440 // type: 'point', 'geojson', or 'countrycoder'
94441 // location: the queried location
94442 // id: a stable identifier for the feature
94443 // feature: the resolved GeoJSON feature
94445 // or `null` if the location is invalid
94449 key: "resolveLocation",
94450 value: function resolveLocation(location) {
94451 var valid = this.validateLocation(location);
94452 if (!valid) return null;
94453 var id = valid.id; // return a result from cache if we can
94455 if (this._cache[id]) {
94456 return Object.assign(valid, {
94457 feature: this._cache[id]
94459 } // a [lon,lat] coordinate pair?
94462 if (valid.type === 'point') {
94463 var RADIUS = 25000; // meters
94467 var area = Math.PI * RADIUS * RADIUS / 1e6; // m² to km²
94469 var feature$1 = this._cache[id] = geojsonPrecision({
94474 area: Number(area.toFixed(2))
94476 geometry: circleToPolygon(location, RADIUS, EDGES)
94478 return Object.assign(valid, {
94480 }); // a .geojson filename?
94481 } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
94482 var _feature = _cloneDeep(feature(id));
94484 var props = _feature.properties; // -> This block of code is weird and requires some explanation. <-
94485 // CountryCoder includes higher level features which are made up of members.
94486 // These features don't have their own geometry, but CountryCoder provides an
94487 // `aggregateFeature` method to combine these members into a MultiPolygon.
94488 // BUT, when we try to actually work with these aggregated MultiPolygons,
94489 // Turf/JSTS gets crashy because of topography bugs.
94490 // SO, we'll aggregate the features ourselves by unioning them together.
94491 // This approach also has the benefit of removing all the internal boaders and
94492 // simplifying the regional polygons a lot.
94494 if (Array.isArray(props.members)) {
94495 var seed = _feature.geometry ? _feature : null;
94496 var aggregate = props.members.reduce(_locationReducer.bind(this), seed);
94497 _feature.geometry = aggregate.geometry;
94498 } // ensure `area` property exists
94502 var _area = geojsonArea.geometry(_feature.geometry) / 1e6; // m² to km²
94505 props.area = Number(_area.toFixed(2));
94506 } // ensure `id` property exists
94511 this._cache[id] = _feature;
94512 return Object.assign(valid, {
94517 if (this._strict) {
94518 throw new Error("resolveLocation: Couldn't resolve location \"".concat(location, "\"."));
94522 } // validateLocationSet
94523 // `locationSet` the locationSet to validate
94525 // Pass a locationSet Object to validate like:
94527 // include: [ Array of locations ],
94528 // exclude: [ Array of locations ]
94531 // Returns a result like:
94533 // type: 'locationset'
94534 // locationSet: the queried locationSet
94535 // id: the stable identifier for the feature
94537 // or `null` if the locationSet is invalid
94541 key: "validateLocationSet",
94542 value: function validateLocationSet(locationSet) {
94543 locationSet = locationSet || {};
94544 var validator = this.validateLocation.bind(this);
94545 var include = (locationSet.include || []).map(validator).filter(Boolean);
94546 var exclude = (locationSet.exclude || []).map(validator).filter(Boolean);
94548 if (!include.length) {
94549 if (this._strict) {
94550 throw new Error("validateLocationSet: LocationSet includes nothing.");
94552 // non-strict mode, replace an empty locationSet with one that includes "the world"
94553 locationSet.include = ['Q2'];
94555 type: 'countrycoder',
94560 } // generate stable identifier
94563 include.sort(_sortLocations);
94564 var id = '+[' + include.map(function (d) {
94566 }).join(',') + ']';
94568 if (exclude.length) {
94569 exclude.sort(_sortLocations);
94570 id += '-[' + exclude.map(function (d) {
94572 }).join(',') + ']';
94576 type: 'locationset',
94577 locationSet: locationSet,
94580 } // resolveLocationSet
94581 // `locationSet` the locationSet to resolve
94583 // Pass a locationSet Object to validate like:
94585 // include: [ Array of locations ],
94586 // exclude: [ Array of locations ]
94589 // Returns a result like:
94591 // type: 'locationset'
94592 // locationSet: the queried locationSet
94593 // id: the stable identifier for the feature
94594 // feature: the resolved GeoJSON feature
94596 // or `null` if the locationSet is invalid
94600 key: "resolveLocationSet",
94601 value: function resolveLocationSet(locationSet) {
94602 locationSet = locationSet || {};
94603 var valid = this.validateLocationSet(locationSet);
94604 if (!valid) return null;
94605 var id = valid.id; // return a result from cache if we can
94607 if (this._cache[id]) {
94608 return Object.assign(valid, {
94609 feature: this._cache[id]
94613 var resolver = this.resolveLocation.bind(this);
94614 var include = (locationSet.include || []).map(resolver).filter(Boolean);
94615 var exclude = (locationSet.exclude || []).map(resolver).filter(Boolean); // return quickly if it's a single included location..
94617 if (include.length === 1 && exclude.length === 0) {
94618 return Object.assign(valid, {
94619 feature: include[0].feature
94621 } // calculate unions
94624 var includeGeoJSON = include.map(function (d) {
94626 }).reduce(_locationReducer.bind(this), null);
94627 var excludeGeoJSON = exclude.map(function (d) {
94629 }).reduce(_locationReducer.bind(this), null); // calculate difference, update `area` and return result
94631 var resultGeoJSON = excludeGeoJSON ? _clip(includeGeoJSON, excludeGeoJSON, 'DIFFERENCE') : includeGeoJSON;
94632 var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km²
94634 resultGeoJSON.id = id;
94635 resultGeoJSON.properties = {
94637 area: Number(area.toFixed(2))
94639 this._cache[id] = resultGeoJSON;
94640 return Object.assign(valid, {
94641 feature: resultGeoJSON
94648 value: function strict(val) {
94649 if (val === undefined) {
94651 return this._strict;
94654 this._strict = val;
94658 // convenience method to access the internal cache
94662 value: function cache() {
94663 return this._cache;
94665 // convenience method to prettyStringify the given object
94669 value: function stringify(obj, options) {
94670 return jsonStringifyPrettyCompact(obj, options);
94675 }(); // Wrap the mfogel/polygon-clipping library and return a GeoJSON feature.
94677 function _clip(a, b, which) {
94679 UNION: index$1.union,
94680 DIFFERENCE: index$1.difference
94682 var coords = fn(a.geometry.coordinates, b.geometry.coordinates);
94687 type: whichType(coords),
94688 coordinates: coords
94690 }; // is this a Polygon or a MultiPolygon?
94692 function whichType(coords) {
94693 var a = Array.isArray(coords);
94694 var b = a && Array.isArray(coords[0]);
94695 var c = b && Array.isArray(coords[0][0]);
94696 var d = c && Array.isArray(coords[0][0][0]);
94697 return d ? 'MultiPolygon' : 'Polygon';
94699 } // Reduce an array of locations into a single GeoJSON feature
94702 function _locationReducer(accumulator, location) {
94703 /* eslint-disable no-console, no-invalid-this */
94707 var resolved = this.resolveLocation(location);
94709 if (!resolved || !resolved.feature) {
94710 console.warn("Warning: Couldn't resolve location \"".concat(location, "\""));
94711 return accumulator;
94714 result = !accumulator ? resolved.feature : _clip(accumulator, resolved.feature, 'UNION');
94716 console.warn("Warning: Error resolving location \"".concat(location, "\""));
94718 result = accumulator;
94722 /* eslint-enable no-console, no-invalid-this */
94725 function _cloneDeep(obj) {
94726 return JSON.parse(JSON.stringify(obj));
94727 } // Sorting the location lists is ok because they end up unioned together.
94728 // This sorting makes it possible to generate a deterministic id.
94731 function _sortLocations(a, b) {
94737 var aRank = rank[a.type];
94738 var bRank = rank[b.type];
94739 return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id);
94743 function uiSuccess(context) {
94745 var dispatch$1 = dispatch('cancel');
94751 ensureOSMCommunityIndex(); // start fetching the data
94753 function ensureOSMCommunityIndex() {
94754 var data = _mainFileFetcher;
94755 return Promise.all([data.get('oci_resources'), data.get('oci_features')]).then(function (vals) {
94756 if (_oci) return _oci;
94757 var ociResources = vals[0].resources;
94758 var loco = new _default$3(vals[1]);
94759 var ociFeatures = {};
94760 Object.values(ociResources).forEach(function (resource) {
94761 var feature = loco.resolveLocationSet(resource.locationSet).feature;
94762 var ociFeature = ociFeatures[feature.id];
94765 ociFeature = JSON.parse(JSON.stringify(feature)); // deep clone
94767 ociFeature.properties.resourceIDs = new Set();
94768 ociFeatures[feature.id] = ociFeature;
94771 ociFeature.properties.resourceIDs.add(resource.id);
94774 features: ociFeatures,
94775 resources: ociResources,
94776 query: whichPolygon_1({
94777 type: 'FeatureCollection',
94778 features: Object.values(ociFeatures)
94782 } // string-to-date parsing in JavaScript is weird
94785 function parseEventDate(when) {
94787 var raw = when.trim();
94790 if (!/Z$/.test(raw)) {
94791 // if no trailing 'Z', add one
94792 raw += 'Z'; // this forces date to be parsed as a UTC date
94795 var parsed = new Date(raw);
94796 return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone
94799 function success(selection) {
94800 var header = selection.append('div').attr('class', 'header fillL');
94801 header.append('h3').html(_t.html('success.just_edited'));
94802 header.append('button').attr('class', 'close').on('click', function () {
94803 return dispatch$1.call('cancel');
94804 }).call(svgIcon('#iD-icon-close'));
94805 var body = selection.append('div').attr('class', 'body save-success fillL');
94806 var summary = body.append('div').attr('class', 'save-summary');
94807 summary.append('h3').html(_t.html('success.thank_you' + (_location ? '_location' : ''), {
94810 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'));
94811 var osm = context.connection();
94813 var changesetURL = osm.changesetURL(_changeset.id);
94814 var table = summary.append('table').attr('class', 'summary-table');
94815 var row = table.append('tr').attr('class', 'summary-row');
94816 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');
94817 var summaryDetail = row.append('td').attr('class', 'cell-detail summary-detail');
94818 summaryDetail.append('a').attr('class', 'cell-detail summary-view-on-osm').attr('target', '_blank').attr('href', changesetURL).html(_t.html('success.view_on_osm'));
94819 summaryDetail.append('div').html(_t.html('success.changeset_id', {
94820 changeset_id: "<a href=\"".concat(changesetURL, "\" target=\"_blank\">").concat(_changeset.id, "</a>")
94821 })); // Get OSM community index features intersecting the map..
94823 ensureOSMCommunityIndex().then(function (oci) {
94824 var communities = [];
94825 var properties = oci.query(context.map().center(), true) || []; // Gather the communities from the result
94827 properties.forEach(function (props) {
94828 var resourceIDs = Array.from(props.resourceIDs);
94829 resourceIDs.forEach(function (resourceID) {
94830 var resource = oci.resources[resourceID];
94832 area: props.area || Infinity,
94833 order: resource.order || 0,
94837 }); // sort communities by feature area ascending, community order descending
94839 communities.sort(function (a, b) {
94840 return a.area - b.area || b.order - a.order;
94842 body.call(showCommunityLinks, communities.map(function (c) {
94848 function showCommunityLinks(selection, resources) {
94849 var communityLinks = selection.append('div').attr('class', 'save-communityLinks');
94850 communityLinks.append('h3').html(_t.html('success.like_osm'));
94851 var table = communityLinks.append('table').attr('class', 'community-table');
94852 var row = table.selectAll('.community-row').data(resources);
94853 var rowEnter = row.enter().append('tr').attr('class', 'community-row');
94854 rowEnter.append('td').attr('class', 'cell-icon community-icon').append('a').attr('target', '_blank').attr('href', function (d) {
94856 }).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', function (d) {
94857 return "#community-".concat(d.type);
94859 var communityDetail = rowEnter.append('td').attr('class', 'cell-detail community-detail');
94860 communityDetail.each(showCommunityDetails);
94861 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'));
94864 function showCommunityDetails(d) {
94865 var selection = select(this);
94866 var communityID = d.id;
94867 var replacements = {
94868 url: linkify(d.url),
94869 signupUrl: linkify(d.signupUrl || d.url)
94871 selection.append('div').attr('class', 'community-name').append('a').attr('target', '_blank').attr('href', d.url).html(_t.html("community.".concat(d.id, ".name")));
94872 var descriptionHTML = _t.html("community.".concat(d.id, ".description"), replacements);
94874 if (d.type === 'reddit') {
94875 // linkify subreddits #4997
94876 descriptionHTML = descriptionHTML.replace(/(\/r\/\w*\/*)/i, function (match) {
94877 return linkify(d.url, match);
94881 selection.append('div').attr('class', 'community-description').html(descriptionHTML);
94883 if (d.extendedDescription || d.languageCodes && d.languageCodes.length) {
94884 selection.append('div').call(uiDisclosure(context, "community-more-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.more')).content(showMore));
94887 var nextEvents = (d.events || []).map(function (event) {
94888 event.date = parseEventDate(event.when);
94890 }).filter(function (event) {
94891 // date is valid and future (or today)
94892 var t = event.date.getTime();
94893 var now = new Date().setHours(0, 0, 0, 0);
94894 return !isNaN(t) && t >= now;
94895 }).sort(function (a, b) {
94896 // sort by date ascending
94897 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
94898 }).slice(0, MAXEVENTS); // limit number of events shown
94900 if (nextEvents.length) {
94901 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);
94904 function showMore(selection) {
94905 var more = selection.selectAll('.community-more').data([0]);
94906 var moreEnter = more.enter().append('div').attr('class', 'community-more');
94908 if (d.extendedDescription) {
94909 moreEnter.append('div').attr('class', 'community-extended-description').html(_t.html("community.".concat(d.id, ".extendedDescription"), replacements));
94912 if (d.languageCodes && d.languageCodes.length) {
94913 var languageList = d.languageCodes.map(function (code) {
94914 return _mainLocalizer.languageName(code);
94916 moreEnter.append('div').attr('class', 'community-languages').html(_t.html('success.languages', {
94917 languages: languageList
94922 function showNextEvents(selection) {
94923 var events = selection.append('div').attr('class', 'community-events');
94924 var item = events.selectAll('.community-event').data(nextEvents);
94925 var itemEnter = item.enter().append('div').attr('class', 'community-event');
94926 itemEnter.append('div').attr('class', 'community-event-name').append('a').attr('target', '_blank').attr('href', function (d) {
94928 }).html(function (d) {
94931 if (d.i18n && d.id) {
94932 name = _t("community.".concat(communityID, ".events.").concat(d.id, ".name"), {
94939 itemEnter.append('div').attr('class', 'community-event-when').html(function (d) {
94947 if (d.date.getHours() || d.date.getMinutes()) {
94948 // include time if it has one
94949 options.hour = 'numeric';
94950 options.minute = 'numeric';
94953 return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
94955 itemEnter.append('div').attr('class', 'community-event-where').html(function (d) {
94956 var where = d.where;
94958 if (d.i18n && d.id) {
94959 where = _t("community.".concat(communityID, ".events.").concat(d.id, ".where"), {
94966 itemEnter.append('div').attr('class', 'community-event-description').html(function (d) {
94967 var description = d.description;
94969 if (d.i18n && d.id) {
94970 description = _t("community.".concat(communityID, ".events.").concat(d.id, ".description"), {
94971 "default": description
94975 return description;
94979 function linkify(url, text) {
94980 text = text || url;
94981 return "<a target=\"_blank\" href=\"".concat(url, "\">").concat(text, "</a>");
94985 success.changeset = function (val) {
94986 if (!arguments.length) return _changeset;
94991 success.location = function (val) {
94992 if (!arguments.length) return _location;
94997 return utilRebind(success, dispatch$1, 'on');
95000 function modeSave(context) {
95004 var keybinding = utilKeybinding('modeSave');
95005 var commit = uiCommit(context).on('cancel', cancel);
95007 var _conflictsUi; // uiConflicts
95014 var uploader = context.uploader().on('saveStarted.modeSave', function () {
95016 }) // fire off some async work that we want to be ready later
95017 .on('willAttemptUpload.modeSave', prepareForSuccess).on('progressChanged.modeSave', showProgress).on('resultNoChanges.modeSave', function () {
95019 }).on('resultErrors.modeSave', showErrors).on('resultConflicts.modeSave', showConflicts).on('resultSuccess.modeSave', showSuccess);
95021 function cancel() {
95022 context.enter(modeBrowse(context));
95025 function showProgress(num, total) {
95026 var modal = context.container().select('.loading-modal .modal-section');
95027 var progress = modal.selectAll('.progress').data([0]); // enter/update
95029 progress.enter().append('div').attr('class', 'progress').merge(progress).text(_t('save.conflict_progress', {
95035 function showConflicts(changeset, conflicts, origChanges) {
95036 var selection = context.container().select('.sidebar').append('div').attr('class', 'sidebar-component');
95037 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
95038 _conflictsUi = uiConflicts(context).conflictList(conflicts).origChanges(origChanges).on('cancel', function () {
95039 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95040 selection.remove();
95042 uploader.cancelConflictResolution();
95043 }).on('save', function () {
95044 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95045 selection.remove();
95046 uploader.processResolvedConflicts(changeset);
95048 selection.call(_conflictsUi);
95051 function showErrors(errors) {
95053 var selection = uiConfirm(context.container());
95054 selection.select('.modal-section.header').append('h3').text(_t('save.error'));
95055 addErrors(selection, errors);
95056 selection.okButton();
95059 function addErrors(selection, data) {
95060 var message = selection.select('.modal-section.message-text');
95061 var items = message.selectAll('.error-container').data(data);
95062 var enter = items.enter().append('div').attr('class', 'error-container');
95063 enter.append('a').attr('class', 'error-description').attr('href', '#').classed('hide-toggle', true).text(function (d) {
95064 return d.msg || _t('save.unknown_error_details');
95065 }).on('click', function (d3_event) {
95066 d3_event.preventDefault();
95067 var error = select(this);
95068 var detail = select(this.nextElementSibling);
95069 var exp = error.classed('expanded');
95070 detail.style('display', exp ? 'none' : 'block');
95071 error.classed('expanded', !exp);
95073 var details = enter.append('div').attr('class', 'error-detail-container').style('display', 'none');
95074 details.append('ul').attr('class', 'error-detail-list').selectAll('li').data(function (d) {
95075 return d.details || [];
95076 }).enter().append('li').attr('class', 'error-detail-item').text(function (d) {
95079 items.exit().remove();
95082 function showSuccess(changeset) {
95085 var ui = _success.changeset(changeset).location(_location).on('cancel', function () {
95086 context.ui().sidebar.hide();
95089 context.enter(modeBrowse(context).sidebar(ui));
95092 function keybindingOn() {
95093 select(document).call(keybinding.on('⎋', cancel, true));
95096 function keybindingOff() {
95097 select(document).call(keybinding.unbind);
95098 } // Reverse geocode current map location so we can display a message on
95099 // the success screen like "Thank you for editing around place, region."
95102 function prepareForSuccess() {
95103 _success = uiSuccess(context);
95105 if (!services.geocoder) return;
95106 services.geocoder.reverse(context.map().center(), function (err, result) {
95107 if (err || !result || !result.address) return;
95108 var addr = result.address;
95109 var place = addr && (addr.town || addr.city || addr.county) || '';
95110 var region = addr && (addr.state || addr.country) || '';
95111 var separator = place && region ? _t('success.thank_you_where.separator') : '';
95112 _location = _t('success.thank_you_where.format', {
95114 separator: separator,
95120 mode.selectedIDs = function () {
95121 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
95124 mode.enter = function () {
95126 context.ui().sidebar.expand();
95129 context.ui().sidebar.show(commit);
95133 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95134 var osm = context.connection();
95141 if (osm.authenticated()) {
95144 osm.authenticate(function (err) {
95154 mode.exit = function () {
95156 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
95157 context.ui().sidebar.hide();
95163 function uiToolOldDrawModes(context) {
95166 label: _t.html('toolbar.add_feature')
95168 var modes = [modeAddPoint(context, {
95169 title: _t.html('modes.add_point.title'),
95171 description: _t.html('modes.add_point.description'),
95172 preset: _mainPresetIndex.item('point'),
95174 }), modeAddLine(context, {
95175 title: _t.html('modes.add_line.title'),
95177 description: _t.html('modes.add_line.description'),
95178 preset: _mainPresetIndex.item('line'),
95180 }), modeAddArea(context, {
95181 title: _t.html('modes.add_area.title'),
95183 description: _t.html('modes.add_area.description'),
95184 preset: _mainPresetIndex.item('area'),
95188 function enabled() {
95189 return osmEditable();
95192 function osmEditable() {
95193 return context.editable();
95196 modes.forEach(function (mode) {
95197 context.keybinding().on(mode.key, function () {
95198 if (!enabled()) return;
95200 if (mode.id === context.mode().id) {
95201 context.enter(modeBrowse(context));
95203 context.enter(mode);
95208 tool.render = function (selection) {
95209 var wrap = selection.append('div').attr('class', 'joined').style('display', 'flex');
95211 var debouncedUpdate = debounce(update, 500, {
95216 context.map().on('move.modes', debouncedUpdate).on('drawn.modes', debouncedUpdate);
95217 context.on('enter.modes', update);
95220 function update() {
95221 var buttons = wrap.selectAll('button.add-button').data(modes, function (d) {
95225 buttons.exit().remove(); // enter
95227 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
95228 return d.id + ' add-button bar-button';
95229 }).on('click.mode-buttons', function (d3_event, d) {
95230 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
95232 var currMode = context.mode().id;
95233 if (/^draw/.test(currMode)) return;
95235 if (d.id === currMode) {
95236 context.enter(modeBrowse(context));
95240 }).call(uiTooltip().placement('bottom').title(function (d) {
95241 return d.description;
95242 }).keys(function (d) {
95244 }).scrollContainer(context.container().select('.top-toolbar')));
95245 buttonsEnter.each(function (d) {
95246 select(this).call(svgIcon('#iD-icon-' + d.button));
95248 buttonsEnter.append('span').attr('class', 'label').html(function (mode) {
95250 }); // if we are adding/removing the buttons, check if toolbar has overflowed
95252 if (buttons.enter().size() || buttons.exit().size()) {
95253 context.ui().checkOverflow('.top-toolbar', true);
95257 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
95259 }).classed('active', function (d) {
95260 return context.mode() && context.mode().button === d.button;
95268 function uiToolNotes(context) {
95271 label: _t.html('modes.add_note.label')
95273 var mode = modeAddNote(context);
95275 function enabled() {
95276 return notesEnabled() && notesEditable();
95279 function notesEnabled() {
95280 var noteLayer = context.layers().layer('notes');
95281 return noteLayer && noteLayer.enabled();
95284 function notesEditable() {
95285 var mode = context.mode();
95286 return context.map().notesEditable() && mode && mode.id !== 'save';
95289 context.keybinding().on(mode.key, function () {
95290 if (!enabled()) return;
95292 if (mode.id === context.mode().id) {
95293 context.enter(modeBrowse(context));
95295 context.enter(mode);
95299 tool.render = function (selection) {
95300 var debouncedUpdate = debounce(update, 500, {
95305 context.map().on('move.notes', debouncedUpdate).on('drawn.notes', debouncedUpdate);
95306 context.on('enter.notes', update);
95309 function update() {
95310 var showNotes = notesEnabled();
95311 var data = showNotes ? [mode] : [];
95312 var buttons = selection.selectAll('button.add-button').data(data, function (d) {
95316 buttons.exit().remove(); // enter
95318 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
95319 return d.id + ' add-button bar-button';
95320 }).on('click.notes', function (d3_event, d) {
95321 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
95323 var currMode = context.mode().id;
95324 if (/^draw/.test(currMode)) return;
95326 if (d.id === currMode) {
95327 context.enter(modeBrowse(context));
95331 }).call(uiTooltip().placement('bottom').title(function (d) {
95332 return d.description;
95333 }).keys(function (d) {
95335 }).scrollContainer(context.container().select('.top-toolbar')));
95336 buttonsEnter.each(function (d) {
95337 select(this).call(svgIcon(d.icon || '#iD-icon-' + d.button));
95338 }); // if we are adding/removing the buttons, check if toolbar has overflowed
95340 if (buttons.enter().size() || buttons.exit().size()) {
95341 context.ui().checkOverflow('.top-toolbar', true);
95345 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
95347 }).classed('active', function (d) {
95348 return context.mode() && context.mode().button === d.button;
95353 tool.uninstall = function () {
95354 context.on('enter.editor.notes', null).on('exit.editor.notes', null).on('enter.notes', null);
95355 context.map().on('move.notes', null).on('drawn.notes', null);
95361 function uiToolSave(context) {
95364 label: _t.html('save.title')
95367 var tooltipBehavior = null;
95368 var history = context.history();
95369 var key = uiCmd('⌘S');
95370 var _numChanges = 0;
95372 function isSaving() {
95373 var mode = context.mode();
95374 return mode && mode.id === 'save';
95377 function isDisabled() {
95378 return _numChanges === 0 || isSaving();
95381 function save(d3_event) {
95382 d3_event.preventDefault();
95384 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
95385 context.enter(modeSave(context));
95389 function bgColor() {
95392 if (_numChanges === 0) {
95394 } else if (_numChanges <= 50) {
95395 step = _numChanges / 50;
95396 return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow
95398 step = Math.min((_numChanges - 50) / 50, 1.0);
95399 return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red
95403 function updateCount() {
95404 var val = history.difference().summary().length;
95405 if (val === _numChanges) return;
95408 if (tooltipBehavior) {
95409 tooltipBehavior.title(_t.html(_numChanges > 0 ? 'save.help' : 'save.no_changes')).keys([key]);
95413 button.classed('disabled', isDisabled()).style('background', bgColor());
95414 button.select('span.count').html(_numChanges);
95418 tool.render = function (selection) {
95419 tooltipBehavior = uiTooltip().placement('bottom').title(_t.html('save.no_changes')).keys([key]).scrollContainer(context.container().select('.top-toolbar'));
95420 var lastPointerUpType;
95421 button = selection.append('button').attr('class', 'save disabled bar-button').on('pointerup', function (d3_event) {
95422 lastPointerUpType = d3_event.pointerType;
95423 }).on('click', function (d3_event) {
95426 if (_numChanges === 0 && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
95427 // there are no tooltips for touch interactions so flash feedback instead
95428 context.ui().flash.duration(2000).iconName('#iD-icon-save').iconClass('disabled').label(_t.html('save.no_changes'))();
95431 lastPointerUpType = null;
95432 }).call(tooltipBehavior);
95433 button.call(svgIcon('#iD-icon-save'));
95434 button.append('span').attr('class', 'count').attr('aria-hidden', 'true').html('0');
95436 context.keybinding().on(key, save, true);
95437 context.history().on('change.save', updateCount);
95438 context.on('enter.save', function () {
95440 button.classed('disabled', isDisabled());
95443 button.call(tooltipBehavior.hide);
95449 tool.uninstall = function () {
95450 context.keybinding().off(key, true);
95451 context.history().on('change.save', null);
95452 context.on('enter.save', null);
95454 tooltipBehavior = null;
95460 function uiToolSidebarToggle(context) {
95462 id: 'sidebar_toggle',
95463 label: _t.html('toolbar.inspect')
95466 tool.render = function (selection) {
95467 selection.append('button').attr('class', 'bar-button').on('click', function () {
95468 context.ui().sidebar.toggle();
95469 }).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')));
95475 function uiToolUndoRedo(context) {
95478 label: _t.html('toolbar.undo_redo')
95483 action: function action() {
95486 annotation: function annotation() {
95487 return context.history().undoAnnotation();
95489 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
95493 action: function action() {
95496 annotation: function annotation() {
95497 return context.history().redoAnnotation();
95499 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
95502 function editable() {
95503 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true
95504 /* ignore min zoom */
95508 tool.render = function (selection) {
95509 var tooltipBehavior = uiTooltip().placement('bottom').title(function (d) {
95510 return d.annotation() ? _t.html(d.id + '.tooltip', {
95511 action: d.annotation()
95512 }) : _t.html(d.id + '.nothing');
95513 }).keys(function (d) {
95515 }).scrollContainer(context.container().select('.top-toolbar'));
95516 var lastPointerUpType;
95517 var buttons = selection.selectAll('button').data(commands).enter().append('button').attr('class', function (d) {
95518 return 'disabled ' + d.id + '-button bar-button';
95519 }).on('pointerup', function (d3_event) {
95520 // `pointerup` is always called before `click`
95521 lastPointerUpType = d3_event.pointerType;
95522 }).on('click', function (d3_event, d) {
95523 d3_event.preventDefault();
95524 var annotation = d.annotation();
95526 if (editable() && annotation) {
95530 if (editable() && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
95531 // there are no tooltips for touch interactions so flash feedback instead
95532 var text = annotation ? _t(d.id + '.tooltip', {
95534 }) : _t(d.id + '.nothing');
95535 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass(annotation ? '' : 'disabled').label(text)();
95538 lastPointerUpType = null;
95539 }).call(tooltipBehavior);
95540 buttons.each(function (d) {
95541 select(this).call(svgIcon('#' + d.icon));
95543 context.keybinding().on(commands[0].cmd, function (d3_event) {
95544 d3_event.preventDefault();
95545 if (editable()) commands[0].action();
95546 }).on(commands[1].cmd, function (d3_event) {
95547 d3_event.preventDefault();
95548 if (editable()) commands[1].action();
95551 var debouncedUpdate = debounce(update, 500, {
95556 context.map().on('move.undo_redo', debouncedUpdate).on('drawn.undo_redo', debouncedUpdate);
95557 context.history().on('change.undo_redo', function (difference) {
95558 if (difference) update();
95560 context.on('enter.undo_redo', update);
95562 function update() {
95563 buttons.classed('disabled', function (d) {
95564 return !editable() || !d.annotation();
95565 }).each(function () {
95566 var selection = select(this);
95568 if (!selection.select('.tooltip.in').empty()) {
95569 selection.call(tooltipBehavior.updateContent);
95575 tool.uninstall = function () {
95576 context.keybinding().off(commands[0].cmd).off(commands[1].cmd);
95577 context.map().on('move.undo_redo', null).on('drawn.undo_redo', null);
95578 context.history().on('change.undo_redo', null);
95579 context.on('enter.undo_redo', null);
95585 function uiTopToolbar(context) {
95586 var sidebarToggle = uiToolSidebarToggle(context),
95587 modes = uiToolOldDrawModes(context),
95588 notes = uiToolNotes(context),
95589 undoRedo = uiToolUndoRedo(context),
95590 save = uiToolSave(context);
95592 function notesEnabled() {
95593 var noteLayer = context.layers().layer('notes');
95594 return noteLayer && noteLayer.enabled();
95597 function topToolbar(bar) {
95598 bar.on('wheel.topToolbar', function (d3_event) {
95599 if (!d3_event.deltaX) {
95600 // translate vertical scrolling into horizontal scrolling in case
95601 // the user doesn't have an input device that can scroll horizontally
95602 bar.node().scrollLeft += d3_event.deltaY;
95606 var debouncedUpdate = debounce(update, 500, {
95611 context.layers().on('change.topToolbar', debouncedUpdate);
95614 function update() {
95615 var tools = [sidebarToggle, 'spacer', modes];
95616 tools.push('spacer');
95618 if (notesEnabled()) {
95619 tools = tools.concat([notes, 'spacer']);
95622 tools = tools.concat([undoRedo, save]);
95623 var toolbarItems = bar.selectAll('.toolbar-item').data(tools, function (d) {
95626 toolbarItems.exit().each(function (d) {
95631 var itemsEnter = toolbarItems.enter().append('div').attr('class', function (d) {
95632 var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
95633 if (d.klass) classes += ' ' + d.klass;
95636 var actionableItems = itemsEnter.filter(function (d) {
95637 return d !== 'spacer';
95639 actionableItems.append('div').attr('class', 'item-content').each(function (d) {
95640 select(this).call(d.render, bar);
95642 actionableItems.append('div').attr('class', 'item-label').html(function (d) {
95651 var sawVersion = null;
95652 var isNewVersion = false;
95653 var isNewUser = false;
95654 function uiVersion(context) {
95655 var currVersion = context.version;
95656 var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
95658 if (sawVersion === null && matchedVersion !== null) {
95659 if (corePreferences('sawVersion')) {
95661 isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
95664 isNewVersion = true;
95667 corePreferences('sawVersion', currVersion);
95668 sawVersion = currVersion;
95671 return function (selection) {
95672 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
95674 if (isNewVersion && !isNewUser) {
95675 selection.append('div').attr('class', 'badge').append('a').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', {
95676 version: currVersion
95677 })).placement('top'));
95682 function uiZoom(context) {
95685 icon: 'iD-icon-plus',
95686 title: _t.html('zoom.in'),
95688 disabled: function disabled() {
95689 return !context.map().canZoomIn();
95691 disabledTitle: _t.html('zoom.disabled.in'),
95695 icon: 'iD-icon-minus',
95696 title: _t.html('zoom.out'),
95698 disabled: function disabled() {
95699 return !context.map().canZoomOut();
95701 disabledTitle: _t.html('zoom.disabled.out'),
95705 function zoomIn(d3_event) {
95706 if (d3_event.shiftKey) return;
95707 d3_event.preventDefault();
95708 context.map().zoomIn();
95711 function zoomOut(d3_event) {
95712 if (d3_event.shiftKey) return;
95713 d3_event.preventDefault();
95714 context.map().zoomOut();
95717 function zoomInFurther(d3_event) {
95718 if (d3_event.shiftKey) return;
95719 d3_event.preventDefault();
95720 context.map().zoomInFurther();
95723 function zoomOutFurther(d3_event) {
95724 if (d3_event.shiftKey) return;
95725 d3_event.preventDefault();
95726 context.map().zoomOutFurther();
95729 return function (selection) {
95730 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function (d) {
95731 if (d.disabled()) {
95732 return d.disabledTitle;
95736 }).keys(function (d) {
95739 var lastPointerUpType;
95740 var buttons = selection.selectAll('button').data(zooms).enter().append('button').attr('class', function (d) {
95742 }).on('pointerup.editor', function (d3_event) {
95743 lastPointerUpType = d3_event.pointerType;
95744 }).on('click.editor', function (d3_event, d) {
95745 if (!d.disabled()) {
95746 d.action(d3_event);
95747 } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
95748 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass('disabled').label(d.disabledTitle)();
95751 lastPointerUpType = null;
95752 }).call(tooltipBehavior);
95753 buttons.each(function (d) {
95754 select(this).call(svgIcon('#' + d.icon, 'light'));
95756 utilKeybinding.plusKeys.forEach(function (key) {
95757 context.keybinding().on([key], zoomIn);
95758 context.keybinding().on([uiCmd('⌥' + key)], zoomInFurther);
95760 utilKeybinding.minusKeys.forEach(function (key) {
95761 context.keybinding().on([key], zoomOut);
95762 context.keybinding().on([uiCmd('⌥' + key)], zoomOutFurther);
95765 function updateButtonStates() {
95766 buttons.classed('disabled', function (d) {
95767 return d.disabled();
95768 }).each(function () {
95769 var selection = select(this);
95771 if (!selection.select('.tooltip.in').empty()) {
95772 selection.call(tooltipBehavior.updateContent);
95777 updateButtonStates();
95778 context.map().on('move.uiZoom', updateButtonStates);
95782 function uiZoomToSelection(context) {
95783 function isDisabled() {
95784 var mode = context.mode();
95785 return !mode || !mode.zoomToSelected;
95788 var _lastPointerUpType;
95790 function pointerup(d3_event) {
95791 _lastPointerUpType = d3_event.pointerType;
95794 function click(d3_event) {
95795 d3_event.preventDefault();
95797 if (isDisabled()) {
95798 if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
95799 context.ui().flash.duration(2000).iconName('#iD-icon-framed-dot').iconClass('disabled').label(_t.html('inspector.zoom_to.no_selection'))();
95802 var mode = context.mode();
95804 if (mode && mode.zoomToSelected) {
95805 mode.zoomToSelected();
95809 _lastPointerUpType = null;
95812 return function (selection) {
95813 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function () {
95814 if (isDisabled()) {
95815 return _t.html('inspector.zoom_to.no_selection');
95818 return _t.html('inspector.zoom_to.title');
95819 }).keys([_t('inspector.zoom_to.key')]);
95820 var button = selection.append('button').on('pointerup', pointerup).on('click', click).call(svgIcon('#iD-icon-framed-dot', 'light')).call(tooltipBehavior);
95822 function setEnabledState() {
95823 button.classed('disabled', isDisabled());
95825 if (!button.select('.tooltip.in').empty()) {
95826 button.call(tooltipBehavior.updateContent);
95830 context.on('enter.uiZoomToSelection', setEnabledState);
95835 function uiPane(id, context) {
95839 var _description = '';
95840 var _iconName = '';
95842 var _sections; // array of uiSection objects
95845 var _paneSelection = select(null);
95853 pane.label = function (val) {
95854 if (!arguments.length) return _label;
95859 pane.key = function (val) {
95860 if (!arguments.length) return _key;
95865 pane.description = function (val) {
95866 if (!arguments.length) return _description;
95867 _description = val;
95871 pane.iconName = function (val) {
95872 if (!arguments.length) return _iconName;
95877 pane.sections = function (val) {
95878 if (!arguments.length) return _sections;
95883 pane.selection = function () {
95884 return _paneSelection;
95887 function hidePane() {
95888 context.ui().togglePanes();
95891 pane.togglePane = function (d3_event) {
95892 if (d3_event) d3_event.preventDefault();
95894 _paneTooltip.hide();
95896 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
95899 pane.renderToggleButton = function (selection) {
95900 if (!_paneTooltip) {
95901 _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_description).keys([_key]);
95904 selection.append('button').on('click', pane.togglePane).call(svgIcon('#' + _iconName, 'light')).call(_paneTooltip);
95907 pane.renderContent = function (selection) {
95908 // override to fully customize content
95910 _sections.forEach(function (section) {
95911 selection.call(section.render);
95916 pane.renderPane = function (selection) {
95917 _paneSelection = selection.append('div').attr('class', 'fillL map-pane hide ' + id + '-pane').attr('pane', id);
95919 var heading = _paneSelection.append('div').attr('class', 'pane-heading');
95921 heading.append('h2').html(_label);
95922 heading.append('button').on('click', hidePane).call(svgIcon('#iD-icon-close'));
95924 _paneSelection.append('div').attr('class', 'pane-content').call(pane.renderContent);
95927 context.keybinding().on(_key, pane.togglePane);
95934 function uiSectionBackgroundDisplayOptions(context) {
95935 var section = uiSection('background-display-options', context).label(_t.html('background.display_options')).disclosureContent(renderDisclosureContent);
95937 var _detected = utilDetect();
95939 var _storedOpacity = corePreferences('background-opacity');
95943 var _maxVal = _detected.cssfilters ? 3 : 1;
95945 var _sliders = _detected.cssfilters ? ['brightness', 'contrast', 'saturation', 'sharpness'] : ['brightness'];
95948 brightness: _storedOpacity !== null ? +_storedOpacity : 1,
95954 function clamp(x, min, max) {
95955 return Math.max(min, Math.min(x, max));
95958 function updateValue(d, val) {
95959 val = clamp(val, _minVal, _maxVal);
95961 context.background()[d](val);
95963 if (d === 'brightness') {
95964 corePreferences('background-opacity', val);
95967 section.reRender();
95970 function renderDisclosureContent(selection) {
95971 var container = selection.selectAll('.display-options-container').data([0]);
95972 var containerEnter = container.enter().append('div').attr('class', 'display-options-container controls-list'); // add slider controls
95974 var slidersEnter = containerEnter.selectAll('.display-control').data(_sliders).enter().append('div').attr('class', function (d) {
95975 return 'display-control display-control-' + d;
95977 slidersEnter.append('h5').html(function (d) {
95978 return _t.html('background.' + d);
95979 }).append('span').attr('class', function (d) {
95980 return 'display-option-value display-option-value-' + d;
95982 var sildersControlEnter = slidersEnter.append('div').attr('class', 'control-wrap');
95983 sildersControlEnter.append('input').attr('class', function (d) {
95984 return 'display-option-input display-option-input-' + d;
95985 }).attr('type', 'range').attr('min', _minVal).attr('max', _maxVal).attr('step', '0.05').on('input', function (d3_event, d) {
95986 var val = select(this).property('value');
95988 if (!val && d3_event && d3_event.target) {
95989 val = d3_event.target.value;
95992 updateValue(d, val);
95994 sildersControlEnter.append('button').attr('title', _t('background.reset')).attr('class', function (d) {
95995 return 'display-option-reset display-option-reset-' + d;
95996 }).on('click', function (d3_event, d) {
95997 if (d3_event.button !== 0) return;
95999 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); // reset all button
96001 containerEnter.append('a').attr('class', 'display-option-resetlink').attr('href', '#').html(_t.html('background.reset_all')).on('click', function (d3_event) {
96002 d3_event.preventDefault();
96004 for (var i = 0; i < _sliders.length; i++) {
96005 updateValue(_sliders[i], 1);
96009 container = containerEnter.merge(container);
96010 container.selectAll('.display-option-input').property('value', function (d) {
96011 return _options[d];
96013 container.selectAll('.display-option-value').html(function (d) {
96014 return Math.floor(_options[d] * 100) + '%';
96016 container.selectAll('.display-option-reset').classed('disabled', function (d) {
96017 return _options[d] === 1;
96018 }); // first time only, set brightness if needed
96020 if (containerEnter.size() && _options.brightness !== 1) {
96021 context.background().brightness(_options.brightness);
96028 function uiSettingsCustomBackground() {
96029 var dispatch$1 = dispatch('change');
96031 function render(selection) {
96032 // keep separate copies of original and current settings
96033 var _origSettings = {
96034 template: corePreferences('background-custom-template')
96036 var _currSettings = {
96037 template: corePreferences('background-custom-template')
96039 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
96040 var modal = uiConfirm(selection).okButton();
96041 modal.classed('settings-modal settings-custom-background', true);
96042 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_background.header'));
96043 var textSection = modal.select('.modal-section.message-text');
96044 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, "`");
96045 textSection.append('div').attr('class', 'instructions-template').html(marked_1(instructions));
96046 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
96048 var buttonSection = modal.select('.modal-section.buttons');
96049 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
96050 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
96051 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
96053 function isSaveDisabled() {
96055 } // restore the original template
96058 function clickCancel() {
96059 textSection.select('.field-template').property('value', _origSettings.template);
96060 corePreferences('background-custom-template', _origSettings.template);
96063 } // accept the current template
96066 function clickSave() {
96067 _currSettings.template = textSection.select('.field-template').property('value');
96068 corePreferences('background-custom-template', _currSettings.template);
96071 dispatch$1.call('change', this, _currSettings);
96075 return utilRebind(render, dispatch$1, 'on');
96078 function uiSectionBackgroundList(context) {
96079 var _backgroundList = select(null);
96081 var _customSource = context.background().findSource('custom');
96083 var _settingsCustomBackground = uiSettingsCustomBackground().on('change', customChanged);
96085 var section = uiSection('background-list', context).label(_t.html('background.backgrounds')).disclosureContent(renderDisclosureContent);
96087 function previousBackgroundID() {
96088 return corePreferences('background-last-used-toggle');
96091 function renderDisclosureContent(selection) {
96092 // the background list
96093 var container = selection.selectAll('.layer-background-list').data([0]);
96094 _backgroundList = container.enter().append('ul').attr('class', 'layer-list layer-background-list').attr('dir', 'auto').merge(container); // add minimap toggle below list
96096 var bgExtrasListEnter = selection.selectAll('.bg-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list bg-extras-list');
96097 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'));
96098 minimapLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96099 d3_event.preventDefault();
96100 uiMapInMap.toggle();
96102 minimapLabelEnter.append('span').html(_t.html('background.minimap.description'));
96103 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'));
96104 panelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96105 d3_event.preventDefault();
96106 context.ui().info.toggle('background');
96108 panelLabelEnter.append('span').html(_t.html('background.panel.description'));
96109 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'));
96110 locPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96111 d3_event.preventDefault();
96112 context.ui().info.toggle('location');
96114 locPanelLabelEnter.append('span').html(_t.html('background.location_panel.description')); // "Info / Report a Problem" link
96116 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'));
96118 _backgroundList.call(drawListItems, 'radio', function (d3_event, d) {
96119 chooseBackground(d);
96121 return !d.isHidden() && !d.overlay;
96125 function setTooltips(selection) {
96126 selection.each(function (d, i, nodes) {
96127 var item = select(this).select('label');
96128 var span = item.select('span');
96129 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
96130 var description = d.description();
96131 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
96132 item.call(uiTooltip().destroyAny);
96134 if (d.id === previousBackgroundID()) {
96135 item.call(uiTooltip().placement(placement).title('<div>' + _t.html('background.switch') + '</div>').keys([uiCmd('⌘' + _t('background.key'))]));
96136 } else if (description || isOverflowing) {
96137 item.call(uiTooltip().placement(placement).title(description || d.label()));
96142 function drawListItems(layerList, type, change, filter) {
96143 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter).sort(function (a, b) {
96144 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
96146 var layerLinks = layerList.selectAll('li') // We have to be a bit inefficient about reordering the list since
96147 // arrow key navigation of radio values likes to work in the order
96148 // they were added, not the display document order.
96149 .data(sources, function (d, i) {
96150 return d.id + '---' + i;
96152 layerLinks.exit().remove();
96153 var enter = layerLinks.enter().append('li').classed('layer-custom', function (d) {
96154 return d.id === 'custom';
96155 }).classed('best', function (d) {
96158 var label = enter.append('label');
96159 label.append('input').attr('type', type).attr('name', 'background-layer').attr('value', function (d) {
96161 }).on('change', change);
96162 label.append('span').html(function (d) {
96165 enter.filter(function (d) {
96166 return d.id === 'custom';
96167 }).append('button').attr('class', 'layer-browse').call(uiTooltip().title(_t.html('settings.custom_background.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', editCustom).call(svgIcon('#iD-icon-more'));
96168 enter.filter(function (d) {
96170 }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('★');
96171 layerList.call(updateLayerSelections);
96174 function updateLayerSelections(selection) {
96175 function active(d) {
96176 return context.background().showsLayer(d);
96179 selection.selectAll('li').classed('active', active).classed('switch', function (d) {
96180 return d.id === previousBackgroundID();
96181 }).call(setTooltips).selectAll('input').property('checked', active);
96184 function chooseBackground(d) {
96185 if (d.id === 'custom' && !d.template()) {
96186 return editCustom();
96189 var previousBackground = context.background().baseLayerSource();
96190 corePreferences('background-last-used-toggle', previousBackground.id);
96191 corePreferences('background-last-used', d.id);
96192 context.background().baseLayerSource(d);
96195 function customChanged(d) {
96196 if (d && d.template) {
96197 _customSource.template(d.template);
96199 chooseBackground(_customSource);
96201 _customSource.template('');
96203 chooseBackground(context.background().findSource('none'));
96207 function editCustom(d3_event) {
96208 d3_event.preventDefault();
96209 context.container().call(_settingsCustomBackground);
96212 context.background().on('change.background_list', function () {
96213 _backgroundList.call(updateLayerSelections);
96215 context.map().on('move.background_list', debounce(function () {
96216 // layers in-view may have changed due to map move
96217 window.requestIdleCallback(section.reRender);
96222 function uiSectionBackgroundOffset(context) {
96223 var section = uiSection('background-offset', context).label(_t.html('background.fix_misalignment')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
96225 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
96227 var _directions = [['top', [0, -0.5]], ['left', [-0.5, 0]], ['right', [0.5, 0]], ['bottom', [0, 0.5]]];
96229 function updateValue() {
96230 var meters = geoOffsetToMeters(context.background().offset());
96231 var x = +meters[0].toFixed(2);
96232 var y = +meters[1].toFixed(2);
96233 context.container().selectAll('.nudge-inner-rect').select('input').classed('error', false).property('value', x + ', ' + y);
96234 context.container().selectAll('.nudge-reset').classed('disabled', function () {
96235 return x === 0 && y === 0;
96239 function resetOffset() {
96240 context.background().offset([0, 0]);
96244 function nudge(d) {
96245 context.background().nudge(d, context.map().zoom());
96249 function inputOffset() {
96250 var input = select(this);
96251 var d = input.node().value;
96252 if (d === '') return resetOffset();
96253 d = d.replace(/;/g, ',').split(',').map(function (n) {
96254 // if n is NaN, it will always get mapped to false.
96255 return !isNaN(n) && n;
96258 if (d.length !== 2 || !d[0] || !d[1]) {
96259 input.classed('error', true);
96263 context.background().offset(geoMetersToOffset(d));
96267 function dragOffset(d3_event) {
96268 if (d3_event.button !== 0) return;
96269 var origin = [d3_event.clientX, d3_event.clientY];
96270 var pointerId = d3_event.pointerId || 'mouse';
96271 context.container().append('div').attr('class', 'nudge-surface');
96272 select(window).on(_pointerPrefix + 'move.drag-bg-offset', pointermove).on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
96274 if (_pointerPrefix === 'pointer') {
96275 select(window).on('pointercancel.drag-bg-offset', pointerup);
96278 function pointermove(d3_event) {
96279 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
96280 var latest = [d3_event.clientX, d3_event.clientY];
96281 var d = [-(origin[0] - latest[0]) / 4, -(origin[1] - latest[1]) / 4];
96286 function pointerup(d3_event) {
96287 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
96288 if (d3_event.button !== 0) return;
96289 context.container().selectAll('.nudge-surface').remove();
96290 select(window).on('.drag-bg-offset', null);
96294 function renderDisclosureContent(selection) {
96295 var container = selection.selectAll('.nudge-container').data([0]);
96296 var containerEnter = container.enter().append('div').attr('class', 'nudge-container');
96297 containerEnter.append('div').attr('class', 'nudge-instructions').html(_t.html('background.offset'));
96298 var nudgeWrapEnter = containerEnter.append('div').attr('class', 'nudge-controls-wrap');
96299 var nudgeEnter = nudgeWrapEnter.append('div').attr('class', 'nudge-outer-rect').on(_pointerPrefix + 'down', dragOffset);
96300 nudgeEnter.append('div').attr('class', 'nudge-inner-rect').append('input').attr('type', 'text').on('change', inputOffset);
96301 nudgeWrapEnter.append('div').selectAll('button').data(_directions).enter().append('button').attr('class', function (d) {
96302 return d[0] + ' nudge';
96303 }).on('click', function (d3_event, d) {
96306 nudgeWrapEnter.append('button').attr('title', _t('background.reset')).attr('class', 'nudge-reset disabled').on('click', function (d3_event) {
96307 d3_event.preventDefault();
96309 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
96313 context.background().on('change.backgroundOffset-update', updateValue);
96317 function uiSectionOverlayList(context) {
96318 var section = uiSection('overlay-list', context).label(_t.html('background.overlays')).disclosureContent(renderDisclosureContent);
96320 var _overlayList = select(null);
96322 function setTooltips(selection) {
96323 selection.each(function (d, i, nodes) {
96324 var item = select(this).select('label');
96325 var span = item.select('span');
96326 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
96327 var description = d.description();
96328 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
96329 item.call(uiTooltip().destroyAny);
96331 if (description || isOverflowing) {
96332 item.call(uiTooltip().placement(placement).title(description || d.name()));
96337 function updateLayerSelections(selection) {
96338 function active(d) {
96339 return context.background().showsLayer(d);
96342 selection.selectAll('li').classed('active', active).call(setTooltips).selectAll('input').property('checked', active);
96345 function chooseOverlay(d3_event, d) {
96346 d3_event.preventDefault();
96347 context.background().toggleOverlayLayer(d);
96349 _overlayList.call(updateLayerSelections);
96351 document.activeElement.blur();
96354 function drawListItems(layerList, type, change, filter) {
96355 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter);
96356 var layerLinks = layerList.selectAll('li').data(sources, function (d) {
96359 layerLinks.exit().remove();
96360 var enter = layerLinks.enter().append('li');
96361 var label = enter.append('label');
96362 label.append('input').attr('type', type).attr('name', 'layers').on('change', change);
96363 label.append('span').html(function (d) {
96366 layerList.selectAll('li').sort(sortSources);
96367 layerList.call(updateLayerSelections);
96369 function sortSources(a, b) {
96370 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
96374 function renderDisclosureContent(selection) {
96375 var container = selection.selectAll('.layer-overlay-list').data([0]);
96376 _overlayList = container.enter().append('ul').attr('class', 'layer-list layer-overlay-list').attr('dir', 'auto').merge(container);
96378 _overlayList.call(drawListItems, 'checkbox', chooseOverlay, function (d) {
96379 return !d.isHidden() && d.overlay;
96383 context.map().on('move.overlay_list', debounce(function () {
96384 // layers in-view may have changed due to map move
96385 window.requestIdleCallback(section.reRender);
96390 function uiPaneBackground(context) {
96391 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)]);
96392 return backgroundPane;
96395 function uiPaneHelp(context) {
96396 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']]];
96398 'help.help.open_data_h': 3,
96399 'help.help.before_start_h': 3,
96400 'help.help.open_source_h': 3,
96401 'help.overview.navigation_h': 3,
96402 'help.overview.features_h': 3,
96403 'help.editing.select_h': 3,
96404 'help.editing.multiselect_h': 3,
96405 'help.editing.undo_redo_h': 3,
96406 'help.editing.save_h': 3,
96407 'help.editing.upload_h': 3,
96408 'help.editing.backups_h': 3,
96409 'help.editing.keyboard_h': 3,
96410 'help.feature_editor.type_h': 3,
96411 'help.feature_editor.fields_h': 3,
96412 'help.feature_editor.tags_h': 3,
96413 'help.points.add_point_h': 3,
96414 'help.points.move_point_h': 3,
96415 'help.points.delete_point_h': 3,
96416 'help.lines.add_line_h': 3,
96417 'help.lines.modify_line_h': 3,
96418 'help.lines.connect_line_h': 3,
96419 'help.lines.disconnect_line_h': 3,
96420 'help.lines.move_line_h': 3,
96421 'help.lines.delete_line_h': 3,
96422 'help.areas.point_or_area_h': 3,
96423 'help.areas.add_area_h': 3,
96424 'help.areas.square_area_h': 3,
96425 'help.areas.modify_area_h': 3,
96426 'help.areas.delete_area_h': 3,
96427 'help.relations.edit_relation_h': 3,
96428 'help.relations.maintain_relation_h': 3,
96429 'help.relations.relation_types_h': 2,
96430 'help.relations.multipolygon_h': 3,
96431 'help.relations.turn_restriction_h': 3,
96432 'help.relations.route_h': 3,
96433 'help.relations.boundary_h': 3,
96434 'help.notes.add_note_h': 3,
96435 'help.notes.update_note_h': 3,
96436 'help.notes.save_note_h': 3,
96437 'help.imagery.sources_h': 3,
96438 'help.imagery.offsets_h': 3,
96439 'help.streetlevel.using_h': 3,
96440 'help.gps.using_h': 3,
96441 'help.qa.tools_h': 3,
96442 'help.qa.issues_h': 3
96443 }; // For each section, squash all the texts into a single markdown document
96445 var docs = docKeys.map(function (key) {
96446 var helpkey = 'help.' + key[0];
96447 var helpPaneReplacements = {
96448 version: context.version
96450 var text = key[1].reduce(function (all, part) {
96451 var subkey = helpkey + '.' + part;
96452 var depth = headings[subkey]; // is this subkey a heading?
96454 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
96456 return all + hhh + helpHtml(subkey, helpPaneReplacements) + '\n\n';
96459 title: _t.html(helpkey + '.title'),
96460 content: marked_1(text.trim()) // use keyboard key styling for shortcuts
96461 .replace(/<code>/g, '<kbd>').replace(/<\/code>/g, '<\/kbd>')
96464 var helpPane = uiPane('help', context).key(_t('help.key')).label(_t.html('help.title')).description(_t.html('help.title')).iconName('iD-icon-help');
96466 helpPane.renderContent = function (content) {
96467 function clickHelp(d, i) {
96468 var rtl = _mainLocalizer.textDirection() === 'rtl';
96469 content.property('scrollTop', 0);
96470 helpPane.selection().select('.pane-heading h2').html(d.title);
96471 body.html(d.content);
96472 body.selectAll('a').attr('target', '_blank');
96473 menuItems.classed('selected', function (m) {
96474 return m.title === d.title;
96479 nav.call(drawNext).call(drawPrevious);
96481 nav.call(drawPrevious).call(drawNext);
96484 function drawNext(selection) {
96485 if (i < docs.length - 1) {
96486 var nextLink = selection.append('a').attr('href', '#').attr('class', 'next').on('click', function (d3_event) {
96487 d3_event.preventDefault();
96488 clickHelp(docs[i + 1], i + 1);
96490 nextLink.append('span').html(docs[i + 1].title).call(svgIcon(rtl ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
96494 function drawPrevious(selection) {
96496 var prevLink = selection.append('a').attr('href', '#').attr('class', 'previous').on('click', function (d3_event) {
96497 d3_event.preventDefault();
96498 clickHelp(docs[i - 1], i - 1);
96500 prevLink.call(svgIcon(rtl ? '#iD-icon-forward' : '#iD-icon-backward', 'inline')).append('span').html(docs[i - 1].title);
96505 function clickWalkthrough(d3_event) {
96506 d3_event.preventDefault();
96507 if (context.inIntro()) return;
96508 context.container().call(uiIntro(context));
96509 context.ui().togglePanes();
96512 function clickShortcuts(d3_event) {
96513 d3_event.preventDefault();
96514 context.container().call(context.ui().shortcuts, true);
96517 var toc = content.append('ul').attr('class', 'toc');
96518 var menuItems = toc.selectAll('li').data(docs).enter().append('li').append('a').attr('href', '#').html(function (d) {
96520 }).on('click', function (d3_event, d) {
96521 d3_event.preventDefault();
96522 clickHelp(d, docs.indexOf(d));
96524 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);
96525 shortcuts.append('div').html(_t.html('shortcuts.title'));
96526 var walkthrough = toc.append('li').attr('class', 'walkthrough').append('a').attr('href', '#').on('click', clickWalkthrough);
96527 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
96528 walkthrough.append('div').html(_t.html('splash.walkthrough'));
96529 var helpContent = content.append('div').attr('class', 'left-content');
96530 var body = helpContent.append('div').attr('class', 'body');
96531 var nav = helpContent.append('div').attr('class', 'nav');
96532 clickHelp(docs[0], 0);
96538 function uiSectionValidationIssues(id, severity, context) {
96540 var section = uiSection(id, context).label(function () {
96541 if (!_issues) return '';
96542 var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
96543 return _t('inspector.title_count', {
96544 title: _t.html('issues.' + severity + 's.list_title'),
96545 count: issueCountText
96547 }).disclosureContent(renderDisclosureContent).shouldDisplay(function () {
96548 return _issues && _issues.length;
96551 function getOptions() {
96553 what: corePreferences('validate-what') || 'edited',
96554 where: corePreferences('validate-where') || 'all'
96556 } // get and cache the issues to display, unordered
96559 function reloadIssues() {
96560 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
96563 function renderDisclosureContent(selection) {
96564 var center = context.map().center();
96565 var graph = context.graph(); // sort issues by distance away from the center of the map
96567 var issues = _issues.map(function withDistance(issue) {
96568 var extent = issue.extent(graph);
96569 var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
96570 return Object.assign(issue, {
96573 }).sort(function byDistance(a, b) {
96574 return a.dist - b.dist;
96575 }); // cut off at 1000
96578 issues = issues.slice(0, 1000); //renderIgnoredIssuesReset(_warningsSelection);
96580 selection.call(drawIssuesList, issues);
96583 function drawIssuesList(selection, issues) {
96584 var list = selection.selectAll('.issues-list').data([0]);
96585 list = list.enter().append('ul').attr('class', 'layer-list issues-list ' + severity + 's-list').merge(list);
96586 var items = list.selectAll('li').data(issues, function (d) {
96590 items.exit().remove(); // Enter
96592 var itemsEnter = items.enter().append('li').attr('class', function (d) {
96593 return 'issue severity-' + d.severity;
96594 }).on('click', function (d3_event, d) {
96595 context.validator().focusIssue(d);
96596 }).on('mouseover', function (d3_event, d) {
96597 utilHighlightEntities(d.entityIds, true, context);
96598 }).on('mouseout', function (d3_event, d) {
96599 utilHighlightEntities(d.entityIds, false, context);
96601 var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
96602 var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
96603 textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
96604 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
96605 select(this).call(svgIcon(iconName));
96607 textEnter.append('span').attr('class', 'issue-message');
96611 .attr('class', 'issue-autofix')
96612 .each(function(d) {
96613 if (!d.autoFix) return;
96616 .attr('title', t('issues.fix_one.title'))
96617 .datum(d.autoFix) // set button datum to the autofix
96618 .attr('class', 'autofix action')
96619 .on('click', function(d3_event, d) {
96620 d3_event.preventDefault();
96621 d3_event.stopPropagation();
96622 var issuesEntityIDs = d.issue.entityIds;
96623 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
96624 context.perform.apply(context, d.autoArgs);
96625 context.validator().validate();
96627 .call(svgIcon('#iD-icon-wrench'));
96632 items = items.merge(itemsEnter).order();
96633 items.selectAll('.issue-message').html(function (d) {
96634 return d.message(context);
96638 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
96639 var autoFixAll = selection.selectAll('.autofix-all')
96640 .data(canAutoFix.length ? [0] : []);
96645 var autoFixAllEnter = autoFixAll.enter()
96646 .insert('div', '.issues-list')
96647 .attr('class', 'autofix-all');
96648 var linkEnter = autoFixAllEnter
96650 .attr('class', 'autofix-all-link')
96651 .attr('href', '#');
96654 .attr('class', 'autofix-all-link-text')
96655 .html(t.html('issues.fix_all.title'));
96658 .attr('class', 'autofix-all-link-icon')
96659 .call(svgIcon('#iD-icon-wrench'));
96660 if (severity === 'warning') {
96661 renderIgnoredIssuesReset(selection);
96664 autoFixAll = autoFixAll
96665 .merge(autoFixAllEnter);
96666 autoFixAll.selectAll('.autofix-all-link')
96667 .on('click', function() {
96668 context.pauseChangeDispatch();
96669 context.perform(actionNoop());
96670 canAutoFix.forEach(function(issue) {
96671 var args = issue.autoFix.autoArgs.slice(); // copy
96672 if (typeof args[args.length - 1] !== 'function') {
96675 args.push(t('issues.fix_all.annotation'));
96676 context.replace.apply(context, args);
96678 context.resumeChangeDispatch();
96679 context.validator().validate();
96684 context.validator().on('validated.uiSectionValidationIssues' + id, function () {
96685 window.requestIdleCallback(function () {
96687 section.reRender();
96690 context.map().on('move.uiSectionValidationIssues' + id, debounce(function () {
96691 window.requestIdleCallback(function () {
96692 if (getOptions().where === 'visible') {
96693 // must refetch issues if they are viewport-dependent
96695 } // always reload list to re-sort-by-distance
96698 section.reRender();
96704 function uiSectionValidationOptions(context) {
96705 var section = uiSection('issues-options', context).content(renderContent);
96707 function renderContent(selection) {
96708 var container = selection.selectAll('.issues-options-container').data([0]);
96709 container = container.enter().append('div').attr('class', 'issues-options-container').merge(container);
96712 values: ['edited', 'all']
96715 values: ['visible', 'all']
96717 var options = container.selectAll('.issues-option').data(data, function (d) {
96720 var optionsEnter = options.enter().append('div').attr('class', function (d) {
96721 return 'issues-option issues-option-' + d.key;
96723 optionsEnter.append('div').attr('class', 'issues-option-title').html(function (d) {
96724 return _t.html('issues.options.' + d.key + '.title');
96726 var valuesEnter = optionsEnter.selectAll('label').data(function (d) {
96727 return d.values.map(function (val) {
96733 }).enter().append('label');
96734 valuesEnter.append('input').attr('type', 'radio').attr('name', function (d) {
96735 return 'issues-option-' + d.key;
96736 }).attr('value', function (d) {
96738 }).property('checked', function (d) {
96739 return getOptions()[d.key] === d.value;
96740 }).on('change', function (d3_event, d) {
96741 updateOptionValue(d3_event, d.key, d.value);
96743 valuesEnter.append('span').html(function (d) {
96744 return _t.html('issues.options.' + d.key + '.' + d.value);
96748 function getOptions() {
96750 what: corePreferences('validate-what') || 'edited',
96752 where: corePreferences('validate-where') || 'all' // 'all', 'visible'
96757 function updateOptionValue(d3_event, d, val) {
96758 if (!val && d3_event && d3_event.target) {
96759 val = d3_event.target.value;
96762 corePreferences('validate-' + d, val);
96763 context.validator().validate();
96769 function uiSectionValidationRules(context) {
96771 var MAXSQUARE = 20;
96772 var DEFAULTSQUARE = 5; // see also unsquare_way.js
96774 var section = uiSection('issues-rules', context).disclosureContent(renderDisclosureContent).label(_t.html('issues.rules.title'));
96776 var _ruleKeys = context.validator().getRuleKeys().filter(function (key) {
96777 return key !== 'maprules';
96778 }).sort(function (key1, key2) {
96779 // alphabetize by localized title
96780 return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
96783 function renderDisclosureContent(selection) {
96784 var container = selection.selectAll('.issues-rulelist-container').data([0]);
96785 var containerEnter = container.enter().append('div').attr('class', 'issues-rulelist-container');
96786 containerEnter.append('ul').attr('class', 'layer-list issue-rules-list');
96787 var ruleLinks = containerEnter.append('div').attr('class', 'issue-rules-links section-footer');
96788 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
96789 d3_event.preventDefault();
96790 context.validator().disableRules(_ruleKeys);
96792 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
96793 d3_event.preventDefault();
96794 context.validator().disableRules([]);
96797 container = container.merge(containerEnter);
96798 container.selectAll('.issue-rules-list').call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
96801 function drawListItems(selection, data, type, name, change, active) {
96802 var items = selection.selectAll('li').data(data); // Exit
96804 items.exit().remove(); // Enter
96806 var enter = items.enter().append('li');
96808 if (name === 'rule') {
96809 enter.call(uiTooltip().title(function (d) {
96810 return _t.html('issues.' + d + '.tip');
96811 }).placement('top'));
96814 var label = enter.append('label');
96815 label.append('input').attr('type', type).attr('name', name).on('change', change);
96816 label.append('span').html(function (d) {
96819 if (d === 'unsquare_way') {
96820 params.val = '<span class="square-degrees"></span>';
96823 return _t.html('issues.' + d + '.title', params);
96826 items = items.merge(enter);
96827 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); // user-configurable square threshold
96829 var degStr = corePreferences('validate-square-degrees');
96831 if (degStr === null) {
96832 degStr = DEFAULTSQUARE.toString();
96835 var span = items.selectAll('.square-degrees');
96836 var input = span.selectAll('.square-degrees-input').data([0]); // enter / update
96838 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) {
96839 d3_event.preventDefault();
96840 d3_event.stopPropagation();
96842 }).on('keyup', function (d3_event) {
96843 if (d3_event.keyCode === 13) {
96848 }).on('blur', changeSquare).merge(input).property('value', degStr);
96851 function changeSquare() {
96852 var input = select(this);
96853 var degStr = utilGetSetValue(input).trim();
96854 var degNum = parseFloat(degStr, 10);
96856 if (!isFinite(degNum)) {
96857 degNum = DEFAULTSQUARE;
96858 } else if (degNum > MAXSQUARE) {
96859 degNum = MAXSQUARE;
96860 } else if (degNum < MINSQUARE) {
96861 degNum = MINSQUARE;
96864 degNum = Math.round(degNum * 10) / 10; // round to 1 decimal
96866 degStr = degNum.toString();
96867 input.property('value', degStr);
96868 corePreferences('validate-square-degrees', degStr);
96869 context.validator().reloadUnsquareIssues();
96872 function isRuleEnabled(d) {
96873 return context.validator().isRuleEnabled(d);
96876 function toggleRule(d3_event, d) {
96877 context.validator().toggleRule(d);
96880 context.validator().on('validated.uiSectionValidationRules', function () {
96881 window.requestIdleCallback(section.reRender);
96886 function uiSectionValidationStatus(context) {
96887 var section = uiSection('issues-status', context).content(renderContent).shouldDisplay(function () {
96888 var issues = context.validator().getIssues(getOptions());
96889 return issues.length === 0;
96892 function getOptions() {
96894 what: corePreferences('validate-what') || 'edited',
96895 where: corePreferences('validate-where') || 'all'
96899 function renderContent(selection) {
96900 var box = selection.selectAll('.box').data([0]);
96901 var boxEnter = box.enter().append('div').attr('class', 'box');
96902 boxEnter.append('div').call(svgIcon('#iD-icon-apply', 'pre-text'));
96903 var noIssuesMessage = boxEnter.append('span');
96904 noIssuesMessage.append('strong').attr('class', 'message');
96905 noIssuesMessage.append('br');
96906 noIssuesMessage.append('span').attr('class', 'details');
96907 renderIgnoredIssuesReset(selection);
96908 setNoIssuesText(selection);
96911 function renderIgnoredIssuesReset(selection) {
96912 var ignoredIssues = context.validator().getIssues({
96915 includeDisabledRules: true,
96916 includeIgnored: 'only'
96918 var resetIgnored = selection.selectAll('.reset-ignored').data(ignoredIssues.length ? [0] : []); // exit
96920 resetIgnored.exit().remove(); // enter
96922 var resetIgnoredEnter = resetIgnored.enter().append('div').attr('class', 'reset-ignored section-footer');
96923 resetIgnoredEnter.append('a').attr('href', '#'); // update
96925 resetIgnored = resetIgnored.merge(resetIgnoredEnter);
96926 resetIgnored.select('a').html(_t('inspector.title_count', {
96927 title: _t.html('issues.reset_ignored'),
96928 count: ignoredIssues.length
96930 resetIgnored.on('click', function (d3_event) {
96931 d3_event.preventDefault();
96932 context.validator().resetIgnoredIssues();
96936 function setNoIssuesText(selection) {
96937 var opts = getOptions();
96939 function checkForHiddenIssues(cases) {
96940 for (var type in cases) {
96941 var hiddenOpts = cases[type];
96942 var hiddenIssues = context.validator().getIssues(hiddenOpts);
96944 if (hiddenIssues.length) {
96945 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.' + type, {
96946 count: hiddenIssues.length.toString()
96952 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.none'));
96957 if (opts.what === 'edited' && opts.where === 'visible') {
96958 messageType = 'edits_in_view';
96959 checkForHiddenIssues({
96971 includeDisabledRules: 'only'
96973 everything_else_elsewhere: {
96977 disabled_rules_elsewhere: {
96980 includeDisabledRules: 'only'
96985 includeIgnored: 'only'
96987 ignored_issues_elsewhere: {
96990 includeIgnored: 'only'
96993 } else if (opts.what === 'edited' && opts.where === 'all') {
96994 messageType = 'edits';
96995 checkForHiddenIssues({
97003 includeDisabledRules: 'only'
97008 includeIgnored: 'only'
97011 } else if (opts.what === 'all' && opts.where === 'visible') {
97012 messageType = 'everything_in_view';
97013 checkForHiddenIssues({
97021 includeDisabledRules: 'only'
97023 disabled_rules_elsewhere: {
97026 includeDisabledRules: 'only'
97031 includeIgnored: 'only'
97033 ignored_issues_elsewhere: {
97036 includeIgnored: 'only'
97039 } else if (opts.what === 'all' && opts.where === 'all') {
97040 messageType = 'everything';
97041 checkForHiddenIssues({
97045 includeDisabledRules: 'only'
97050 includeIgnored: 'only'
97055 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
97056 messageType = 'no_edits';
97059 selection.select('.box .message').html(_t.html('issues.no_issues.message.' + messageType));
97062 context.validator().on('validated.uiSectionValidationStatus', function () {
97063 window.requestIdleCallback(section.reRender);
97065 context.map().on('move.uiSectionValidationStatus', debounce(function () {
97066 window.requestIdleCallback(section.reRender);
97071 function uiPaneIssues(context) {
97072 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)]);
97076 function uiSettingsCustomData(context) {
97077 var dispatch$1 = dispatch('change');
97079 function render(selection) {
97080 var dataLayer = context.layers().layer('data'); // keep separate copies of original and current settings
97082 var _origSettings = {
97083 fileList: dataLayer && dataLayer.fileList() || null,
97084 url: corePreferences('settings-custom-data-url')
97086 var _currSettings = {
97087 fileList: dataLayer && dataLayer.fileList() || null,
97088 url: corePreferences('settings-custom-data-url')
97089 }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
97091 var modal = uiConfirm(selection).okButton();
97092 modal.classed('settings-modal settings-custom-data', true);
97093 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_data.header'));
97094 var textSection = modal.select('.modal-section.message-text');
97095 textSection.append('pre').attr('class', 'instructions-file').html(_t.html('settings.custom_data.file.instructions'));
97096 textSection.append('input').attr('class', 'field-file').attr('type', 'file').property('files', _currSettings.fileList) // works for all except IE11
97097 .on('change', function (d3_event) {
97098 var files = d3_event.target.files;
97100 if (files && files.length) {
97101 _currSettings.url = '';
97102 textSection.select('.field-url').property('value', '');
97103 _currSettings.fileList = files;
97105 _currSettings.fileList = null;
97108 textSection.append('h4').html(_t.html('settings.custom_data.or'));
97109 textSection.append('pre').attr('class', 'instructions-url').html(_t.html('settings.custom_data.url.instructions'));
97110 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
97112 var buttonSection = modal.select('.modal-section.buttons');
97113 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
97114 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
97115 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
97117 function isSaveDisabled() {
97119 } // restore the original url
97122 function clickCancel() {
97123 textSection.select('.field-url').property('value', _origSettings.url);
97124 corePreferences('settings-custom-data-url', _origSettings.url);
97127 } // accept the current url
97130 function clickSave() {
97131 _currSettings.url = textSection.select('.field-url').property('value').trim(); // one or the other but not both
97133 if (_currSettings.url) {
97134 _currSettings.fileList = null;
97137 if (_currSettings.fileList) {
97138 _currSettings.url = '';
97141 corePreferences('settings-custom-data-url', _currSettings.url);
97144 dispatch$1.call('change', this, _currSettings);
97148 return utilRebind(render, dispatch$1, 'on');
97151 function uiSectionDataLayers(context) {
97152 var settingsCustomData = uiSettingsCustomData(context).on('change', customChanged);
97153 var layers = context.layers();
97154 var section = uiSection('data-layers', context).label(_t.html('map_data.data_layers')).disclosureContent(renderDisclosureContent);
97156 function renderDisclosureContent(selection) {
97157 var container = selection.selectAll('.data-layer-container').data([0]);
97158 container.enter().append('div').attr('class', 'data-layer-container').merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems) // Beta - Detroit mapping challenge
97159 .call(drawPanelItems);
97162 function showsLayer(which) {
97163 var layer = layers.layer(which);
97166 return layer.enabled();
97172 function setLayer(which, enabled) {
97173 // Don't allow layer changes while drawing - #6584
97174 var mode = context.mode();
97175 if (mode && /^draw/.test(mode.id)) return;
97176 var layer = layers.layer(which);
97179 layer.enabled(enabled);
97181 if (!enabled && (which === 'osm' || which === 'notes')) {
97182 context.enter(modeBrowse(context));
97187 function toggleLayer(which) {
97188 setLayer(which, !showsLayer(which));
97191 function drawOsmItems(selection) {
97192 var osmKeys = ['osm', 'notes'];
97193 var osmLayers = layers.all().filter(function (obj) {
97194 return osmKeys.indexOf(obj.id) !== -1;
97196 var ul = selection.selectAll('.layer-list-osm').data([0]);
97197 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-osm').merge(ul);
97198 var li = ul.selectAll('.list-item').data(osmLayers);
97199 li.exit().remove();
97200 var liEnter = li.enter().append('li').attr('class', function (d) {
97201 return 'list-item list-item-' + d.id;
97203 var labelEnter = liEnter.append('label').each(function (d) {
97204 if (d.id === 'osm') {
97205 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]).placement('bottom'));
97207 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
97210 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97213 labelEnter.append('span').html(function (d) {
97214 return _t.html('map_data.layers.' + d.id + '.title');
97217 li.merge(liEnter).classed('active', function (d) {
97218 return d.layer.enabled();
97219 }).selectAll('input').property('checked', function (d) {
97220 return d.layer.enabled();
97224 function drawQAItems(selection) {
97225 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
97226 var qaLayers = layers.all().filter(function (obj) {
97227 return qaKeys.indexOf(obj.id) !== -1;
97229 var ul = selection.selectAll('.layer-list-qa').data([0]);
97230 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-qa').merge(ul);
97231 var li = ul.selectAll('.list-item').data(qaLayers);
97232 li.exit().remove();
97233 var liEnter = li.enter().append('li').attr('class', function (d) {
97234 return 'list-item list-item-' + d.id;
97236 var labelEnter = liEnter.append('label').each(function (d) {
97237 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
97239 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97242 labelEnter.append('span').html(function (d) {
97243 return _t.html('map_data.layers.' + d.id + '.title');
97246 li.merge(liEnter).classed('active', function (d) {
97247 return d.layer.enabled();
97248 }).selectAll('input').property('checked', function (d) {
97249 return d.layer.enabled();
97251 } // Beta feature - sample vector layers to support Detroit Mapping Challenge
97252 // https://github.com/osmus/detroit-mapping-challenge
97255 function drawVectorItems(selection) {
97256 var dataLayer = layers.layer('data');
97258 name: 'Detroit Neighborhoods/Parks',
97259 src: 'neighborhoods-parks',
97260 tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
97261 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'
97263 name: 'Detroit Composite POIs',
97264 src: 'composite-poi',
97265 tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
97266 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'
97268 name: 'Detroit All-The-Places POIs',
97269 src: 'alltheplaces-poi',
97270 tooltip: 'Public domain business location data created by web scrapers.',
97271 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'
97272 }]; // Only show this if the map is around Detroit..
97274 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
97275 var showVectorItems = context.map().zoom() > 9 && detroit.contains(context.map().center());
97276 var container = selection.selectAll('.vectortile-container').data(showVectorItems ? [0] : []);
97277 container.exit().remove();
97278 var containerEnter = container.enter().append('div').attr('class', 'vectortile-container');
97279 containerEnter.append('h4').attr('class', 'vectortile-header').html('Detroit Vector Tiles (Beta)');
97280 containerEnter.append('ul').attr('class', 'layer-list layer-list-vectortile');
97281 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');
97282 container = container.merge(containerEnter);
97283 var ul = container.selectAll('.layer-list-vectortile');
97284 var li = ul.selectAll('.list-item').data(vtData);
97285 li.exit().remove();
97286 var liEnter = li.enter().append('li').attr('class', function (d) {
97287 return 'list-item list-item-' + d.src;
97289 var labelEnter = liEnter.append('label').each(function (d) {
97290 select(this).call(uiTooltip().title(d.tooltip).placement('top'));
97292 labelEnter.append('input').attr('type', 'radio').attr('name', 'vectortile').on('change', selectVTLayer);
97293 labelEnter.append('span').html(function (d) {
97297 li.merge(liEnter).classed('active', isVTLayerSelected).selectAll('input').property('checked', isVTLayerSelected);
97299 function isVTLayerSelected(d) {
97300 return dataLayer && dataLayer.template() === d.template;
97303 function selectVTLayer(d3_event, d) {
97304 corePreferences('settings-custom-data-url', d.template);
97307 dataLayer.template(d.template, d.src);
97308 dataLayer.enabled(true);
97313 function drawCustomDataItems(selection) {
97314 var dataLayer = layers.layer('data');
97315 var hasData = dataLayer && dataLayer.hasData();
97316 var showsData = hasData && dataLayer.enabled();
97317 var ul = selection.selectAll('.layer-list-data').data(dataLayer ? [0] : []); // Exit
97319 ul.exit().remove(); // Enter
97321 var ulEnter = ul.enter().append('ul').attr('class', 'layer-list layer-list-data');
97322 var liEnter = ulEnter.append('li').attr('class', 'list-item-data');
97323 var labelEnter = liEnter.append('label').call(uiTooltip().title(_t.html('map_data.layers.custom.tooltip')).placement('top'));
97324 labelEnter.append('input').attr('type', 'checkbox').on('change', function () {
97325 toggleLayer('data');
97327 labelEnter.append('span').html(_t.html('map_data.layers.custom.title'));
97328 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', editCustom).call(svgIcon('#iD-icon-more'));
97329 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) {
97330 if (select(this).classed('disabled')) return;
97331 d3_event.preventDefault();
97332 d3_event.stopPropagation();
97333 dataLayer.fitZoom();
97334 }).call(svgIcon('#iD-icon-framed-dot', 'monochrome')); // Update
97336 ul = ul.merge(ulEnter);
97337 ul.selectAll('.list-item-data').classed('active', showsData).selectAll('label').classed('deemphasize', !hasData).selectAll('input').property('disabled', !hasData).property('checked', showsData);
97338 ul.selectAll('button.zoom-to-data').classed('disabled', !hasData);
97341 function editCustom(d3_event) {
97342 d3_event.preventDefault();
97343 context.container().call(settingsCustomData);
97346 function customChanged(d) {
97347 var dataLayer = layers.layer('data');
97350 dataLayer.url(d.url);
97351 } else if (d && d.fileList) {
97352 dataLayer.fileList(d.fileList);
97356 function drawPanelItems(selection) {
97357 var panelsListEnter = selection.selectAll('.md-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list md-extras-list');
97358 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'));
97359 historyPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97360 d3_event.preventDefault();
97361 context.ui().info.toggle('history');
97363 historyPanelLabelEnter.append('span').html(_t.html('map_data.history_panel.title'));
97364 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'));
97365 measurementPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97366 d3_event.preventDefault();
97367 context.ui().info.toggle('measurement');
97369 measurementPanelLabelEnter.append('span').html(_t.html('map_data.measurement_panel.title'));
97372 context.layers().on('change.uiSectionDataLayers', section.reRender);
97373 context.map().on('move.uiSectionDataLayers', debounce(function () {
97374 // Detroit layers may have moved in or out of view
97375 window.requestIdleCallback(section.reRender);
97380 function uiSectionMapFeatures(context) {
97381 var _features = context.features().keys();
97383 var section = uiSection('map-features', context).label(_t.html('map_data.map_features')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97385 function renderDisclosureContent(selection) {
97386 var container = selection.selectAll('.layer-feature-list-container').data([0]);
97387 var containerEnter = container.enter().append('div').attr('class', 'layer-feature-list-container');
97388 containerEnter.append('ul').attr('class', 'layer-list layer-feature-list');
97389 var footer = containerEnter.append('div').attr('class', 'feature-list-links section-footer');
97390 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
97391 d3_event.preventDefault();
97392 context.features().disableAll();
97394 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
97395 d3_event.preventDefault();
97396 context.features().enableAll();
97399 container = container.merge(containerEnter);
97400 container.selectAll('.layer-feature-list').call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
97403 function drawListItems(selection, data, type, name, change, active) {
97404 var items = selection.selectAll('li').data(data); // Exit
97406 items.exit().remove(); // Enter
97408 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
97409 var tip = _t.html(name + '.' + d + '.tooltip');
97411 if (autoHiddenFeature(d)) {
97412 var msg = showsLayer('osm') ? _t.html('map_data.autohidden') : _t.html('map_data.osmhidden');
97413 tip += '<div>' + msg + '</div>';
97417 }).placement('top'));
97418 var label = enter.append('label');
97419 label.append('input').attr('type', type).attr('name', name).on('change', change);
97420 label.append('span').html(function (d) {
97421 return _t.html(name + '.' + d + '.description');
97424 items = items.merge(enter);
97425 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', autoHiddenFeature);
97428 function autoHiddenFeature(d) {
97429 return context.features().autoHidden(d);
97432 function showsFeature(d) {
97433 return context.features().enabled(d);
97436 function clickFeature(d3_event, d) {
97437 context.features().toggle(d);
97440 function showsLayer(id) {
97441 var layer = context.layers().layer(id);
97442 return layer && layer.enabled();
97446 context.features().on('change.map_features', section.reRender);
97450 function uiSectionMapStyleOptions(context) {
97451 var section = uiSection('fill-area', context).label(_t.html('map_data.style_options')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97453 function renderDisclosureContent(selection) {
97454 var container = selection.selectAll('.layer-fill-list').data([0]);
97455 container.enter().append('ul').attr('class', 'layer-list layer-fill-list').merge(container).call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
97456 var container2 = selection.selectAll('.layer-visual-diff-list').data([0]);
97457 container2.enter().append('ul').attr('class', 'layer-list layer-visual-diff-list').merge(container2).call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function () {
97458 return context.surface().classed('highlight-edited');
97462 function drawListItems(selection, data, type, name, change, active) {
97463 var items = selection.selectAll('li').data(data); // Exit
97465 items.exit().remove(); // Enter
97467 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
97468 return _t.html(name + '.' + d + '.tooltip');
97469 }).keys(function (d) {
97470 var key = d === 'wireframe' ? _t('area_fill.wireframe.key') : null;
97471 if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
97472 return key ? [key] : null;
97473 }).placement('top'));
97474 var label = enter.append('label');
97475 label.append('input').attr('type', type).attr('name', name).on('change', change);
97476 label.append('span').html(function (d) {
97477 return _t.html(name + '.' + d + '.description');
97480 items = items.merge(enter);
97481 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false);
97484 function isActiveFill(d) {
97485 return context.map().activeAreaFill() === d;
97488 function toggleHighlightEdited(d3_event) {
97489 d3_event.preventDefault();
97490 context.map().toggleHighlightEdited();
97493 function setFill(d3_event, d) {
97494 context.map().activeAreaFill(d);
97497 context.map().on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
97501 function uiSectionPhotoOverlays(context) {
97502 var layers = context.layers();
97503 var section = uiSection('photo-overlays', context).label(_t.html('photo_overlays.title')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97505 function renderDisclosureContent(selection) {
97506 var container = selection.selectAll('.photo-overlay-container').data([0]);
97507 container.enter().append('div').attr('class', 'photo-overlay-container').merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter);
97510 function drawPhotoItems(selection) {
97511 var photoKeys = context.photos().overlayLayerIDs();
97512 var photoLayers = layers.all().filter(function (obj) {
97513 return photoKeys.indexOf(obj.id) !== -1;
97515 var data = photoLayers.filter(function (obj) {
97516 return obj.layer.supported();
97519 function layerSupported(d) {
97520 return d.layer && d.layer.supported();
97523 function layerEnabled(d) {
97524 return layerSupported(d) && d.layer.enabled();
97527 var ul = selection.selectAll('.layer-list-photos').data([0]);
97528 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photos').merge(ul);
97529 var li = ul.selectAll('.list-item-photos').data(data);
97530 li.exit().remove();
97531 var liEnter = li.enter().append('li').attr('class', function (d) {
97532 var classes = 'list-item-photos list-item-' + d.id;
97534 if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
97535 classes += ' indented';
97540 var labelEnter = liEnter.append('label').each(function (d) {
97542 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';
97543 select(this).call(uiTooltip().title(_t.html(titleID)).placement('top'));
97545 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97548 labelEnter.append('span').html(function (d) {
97550 if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
97551 return _t.html(id.replace(/-/g, '_') + '.title');
97554 li.merge(liEnter).classed('active', layerEnabled).selectAll('input').property('checked', layerEnabled);
97557 function drawPhotoTypeItems(selection) {
97558 var data = context.photos().allPhotoTypes();
97560 function typeEnabled(d) {
97561 return context.photos().showsPhotoType(d);
97564 var ul = selection.selectAll('.layer-list-photo-types').data([0]);
97565 ul.exit().remove();
97566 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photo-types').merge(ul);
97567 var li = ul.selectAll('.list-item-photo-types').data(context.photos().shouldFilterByPhotoType() ? data : []);
97568 li.exit().remove();
97569 var liEnter = li.enter().append('li').attr('class', function (d) {
97570 return 'list-item-photo-types list-item-' + d;
97572 var labelEnter = liEnter.append('label').each(function (d) {
97573 select(this).call(uiTooltip().title(_t.html('photo_overlays.photo_type.' + d + '.tooltip')).placement('top'));
97575 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97576 context.photos().togglePhotoType(d);
97578 labelEnter.append('span').html(function (d) {
97579 return _t.html('photo_overlays.photo_type.' + d + '.title');
97582 li.merge(liEnter).classed('active', typeEnabled).selectAll('input').property('checked', typeEnabled);
97585 function drawDateFilter(selection) {
97586 var data = context.photos().dateFilters();
97588 function filterEnabled(d) {
97589 return context.photos().dateFilterValue(d);
97592 var ul = selection.selectAll('.layer-list-date-filter').data([0]);
97593 ul.exit().remove();
97594 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-date-filter').merge(ul);
97595 var li = ul.selectAll('.list-item-date-filter').data(context.photos().shouldFilterByDate() ? data : []);
97596 li.exit().remove();
97597 var liEnter = li.enter().append('li').attr('class', 'list-item-date-filter');
97598 var labelEnter = liEnter.append('label').each(function (d) {
97599 select(this).call(uiTooltip().title(_t.html('photo_overlays.date_filter.' + d + '.tooltip')).placement('top'));
97601 labelEnter.append('span').html(function (d) {
97602 return _t.html('photo_overlays.date_filter.' + d + '.title');
97604 labelEnter.append('input').attr('type', 'date').attr('class', 'list-item-input').attr('placeholder', _t('units.year_month_day')).call(utilNoAuto).each(function (d) {
97605 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
97606 }).on('change', function (d3_event, d) {
97607 var value = utilGetSetValue(select(this)).trim();
97608 context.photos().setDateFilter(d, value, true); // reload the displayed dates
97610 li.selectAll('input').each(function (d) {
97611 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
97614 li = li.merge(liEnter).classed('active', filterEnabled);
97617 function drawUsernameFilter(selection) {
97618 function filterEnabled() {
97619 return context.photos().usernames();
97622 var ul = selection.selectAll('.layer-list-username-filter').data([0]);
97623 ul.exit().remove();
97624 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-username-filter').merge(ul);
97625 var li = ul.selectAll('.list-item-username-filter').data(context.photos().shouldFilterByUsername() ? ['username-filter'] : []);
97626 li.exit().remove();
97627 var liEnter = li.enter().append('li').attr('class', 'list-item-username-filter');
97628 var labelEnter = liEnter.append('label').each(function () {
97629 select(this).call(uiTooltip().title(_t.html('photo_overlays.username_filter.tooltip')).placement('top'));
97631 labelEnter.append('span').html(_t.html('photo_overlays.username_filter.title'));
97632 labelEnter.append('input').attr('type', 'text').attr('class', 'list-item-input').call(utilNoAuto).property('value', usernameValue).on('change', function () {
97633 var value = select(this).property('value');
97634 context.photos().setUsernameFilter(value, true);
97635 select(this).property('value', usernameValue);
97637 li.merge(liEnter).classed('active', filterEnabled);
97639 function usernameValue() {
97640 var usernames = context.photos().usernames();
97641 if (usernames) return usernames.join('; ');
97646 function toggleLayer(which) {
97647 setLayer(which, !showsLayer(which));
97650 function showsLayer(which) {
97651 var layer = layers.layer(which);
97654 return layer.enabled();
97660 function setLayer(which, enabled) {
97661 var layer = layers.layer(which);
97664 layer.enabled(enabled);
97668 context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
97669 context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
97673 function uiPaneMapData(context) {
97674 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)]);
97675 return mapDataPane;
97678 function uiSectionPrivacy(context) {
97679 var section = uiSection('preferences-third-party', context).label(_t.html('preferences.privacy.title')).disclosureContent(renderDisclosureContent);
97681 var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
97683 function renderDisclosureContent(selection) {
97685 var privacyOptionsListEnter = selection.selectAll('.privacy-options-list').data([0]).enter().append('ul').attr('class', 'layer-list privacy-options-list');
97686 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'));
97687 thirdPartyIconsEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97688 d3_event.preventDefault();
97689 _showThirdPartyIcons = _showThirdPartyIcons === 'true' ? 'false' : 'true';
97690 corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
97693 thirdPartyIconsEnter.append('span').html(_t.html('preferences.privacy.third_party_icons.description')); // Privacy Policy link
97695 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'));
97698 function update() {
97699 selection.selectAll('.privacy-third-party-icons-item').classed('active', _showThirdPartyIcons === 'true').select('input').property('checked', _showThirdPartyIcons === 'true');
97706 function uiPanePreferences(context) {
97707 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)]);
97708 return preferencesPane;
97711 function uiInit(context) {
97712 var _initCounter = 0;
97713 var _needWidth = {};
97715 var _lastPointerType;
97717 function render(container) {
97718 container.on('click.ui', function (d3_event) {
97719 // we're only concerned with the primary mouse button
97720 if (d3_event.button !== 0) return;
97721 if (!d3_event.composedPath) return; // some targets have default click events we don't want to override
97723 var isOkayTarget = d3_event.composedPath().some(function (node) {
97724 // we only care about element nodes
97725 return node.nodeType === 1 && ( // clicking <input> focuses it and/or changes a value
97726 node.nodeName === 'INPUT' || // clicking <label> affects its <input> by default
97727 node.nodeName === 'LABEL' || // clicking <a> opens a hyperlink by default
97728 node.nodeName === 'A');
97730 if (isOkayTarget) return; // disable double-tap-to-zoom on touchscreens
97732 d3_event.preventDefault();
97734 var detected = utilDetect(); // only WebKit supports gesture events
97736 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
97737 // but we only need to do this on desktop Safari anyway. – #7694
97738 !detected.isMobileWebKit) {
97739 // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
97740 // CSS property, but on desktop Safari we need to manually cancel the
97741 // default gesture events.
97742 container.on('gesturestart.ui gesturechange.ui gestureend.ui', function (d3_event) {
97743 // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
97744 d3_event.preventDefault();
97748 if ('PointerEvent' in window) {
97749 select(window).on('pointerdown.ui pointerup.ui', function (d3_event) {
97750 var pointerType = d3_event.pointerType || 'mouse';
97752 if (_lastPointerType !== pointerType) {
97753 _lastPointerType = pointerType;
97754 container.attr('pointer', pointerType);
97758 _lastPointerType = 'mouse';
97759 container.attr('pointer', 'mouse');
97762 container.attr('lang', _mainLocalizer.localeCode()).attr('dir', _mainLocalizer.textDirection()); // setup fullscreen keybindings (no button shown at this time)
97764 container.call(uiFullScreen(context));
97765 var map = context.map();
97766 map.redrawEnable(false); // don't draw until we've set zoom/lat/long
97768 map.on('hitMinZoom.ui', function () {
97769 ui.flash.iconName('#iD-icon-no').label(_t.html('cannot_zoom'))();
97771 container.append('svg').attr('id', 'ideditor-defs').call(ui.svgDefs);
97772 container.append('div').attr('class', 'sidebar').call(ui.sidebar);
97773 var content = container.append('div').attr('class', 'main-content active'); // Top toolbar
97775 content.append('div').attr('class', 'top-toolbar-wrap').append('div').attr('class', 'top-toolbar fillD').call(uiTopToolbar(context));
97776 content.append('div').attr('class', 'main-map').attr('dir', 'ltr').call(map);
97777 var overMap = content.append('div').attr('class', 'over-map'); // HACK: Mobile Safari 14 likes to select anything selectable when long-
97778 // pressing, even if it's not targeted. This conflicts with long-pressing
97779 // to show the edit menu. We add a selectable offscreen element as the first
97780 // child to trick Safari into not showing the selection UI.
97782 overMap.append('div').attr('class', 'select-trap').text('t');
97783 overMap.append('div').attr('class', 'spinner').call(uiSpinner(context));
97784 overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Map controls
97786 var controls = overMap.append('div').attr('class', 'map-controls');
97787 controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
97788 controls.append('div').attr('class', 'map-control zoom-to-selection-control').call(uiZoomToSelection(context));
97789 controls.append('div').attr('class', 'map-control geolocate-control').call(uiGeolocate(context)); // Add panes
97790 // This should happen after map is initialized, as some require surface()
97792 var panes = overMap.append('div').attr('class', 'map-panes');
97793 var uiPanes = [uiPaneBackground(context), uiPaneMapData(context), uiPaneIssues(context), uiPanePreferences(context), uiPaneHelp(context)];
97794 uiPanes.forEach(function (pane) {
97795 controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
97796 panes.call(pane.renderPane);
97798 ui.info = uiInfo(context); // Add absolutely-positioned elements that sit on top of the map
97799 // This should happen after the map is ready (center/zoom)
97801 overMap.call(uiMapInMap(context)).call(ui.info).call(uiNotice(context));
97802 overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left, 'ar'=right
97803 .classed('hide', true).call(ui.photoviewer); // Add footer
97805 var about = content.append('div').attr('class', 'map-footer');
97806 about.append('div').attr('class', 'api-status').call(uiStatus(context));
97807 var footer = about.append('div').attr('class', 'map-footer-bar fillD');
97808 footer.append('div').attr('class', 'flash-wrap footer-hide');
97809 var footerWrap = footer.append('div').attr('class', 'main-footer-wrap footer-show');
97810 footerWrap.append('div').attr('class', 'scale-block').call(uiScale(context));
97811 var aboutList = footerWrap.append('div').attr('class', 'info-block').append('ul').attr('class', 'map-footer-list');
97812 aboutList.append('li').attr('class', 'user-list').call(uiContributors(context));
97813 var apiConnections = context.apiConnections();
97815 if (apiConnections && apiConnections.length > 1) {
97816 aboutList.append('li').attr('class', 'source-switch').call(uiSourceSwitch(context).keys(apiConnections));
97819 aboutList.append('li').attr('class', 'issues-info').call(uiIssuesInfo(context));
97820 aboutList.append('li').attr('class', 'feature-warning').call(uiFeatureInfo(context));
97821 var issueLinks = aboutList.append('li');
97822 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'));
97823 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'));
97824 aboutList.append('li').attr('class', 'version').call(uiVersion(context));
97826 if (!context.embed()) {
97827 aboutList.call(uiAccount(context));
97828 } // Setup map dimensions and move map to initial center/zoom.
97829 // This should happen after .main-content and toolbars exist.
97833 map.redrawEnable(true);
97834 ui.hash = behaviorHash(context);
97837 if (!ui.hash.hadHash) {
97838 map.centerZoom([0, 0], 2);
97842 window.onbeforeunload = function () {
97843 return context.save();
97846 window.onunload = function () {
97847 context.history().unlock();
97850 select(window).on('resize.editor', function () {
97853 var panPixels = 80;
97854 context.keybinding().on('⌫', function (d3_event) {
97855 d3_event.preventDefault();
97856 }).on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle) // #5663, #6864 - common QWERTY, AZERTY
97857 .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) {
97859 d3_event.stopImmediatePropagation();
97860 d3_event.preventDefault();
97863 var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
97865 if (previousBackground) {
97866 var currentBackground = context.background().baseLayerSource();
97867 corePreferences('background-last-used-toggle', currentBackground.id);
97868 corePreferences('background-last-used', previousBackground.id);
97869 context.background().baseLayerSource(previousBackground);
97871 }).on(_t('area_fill.wireframe.key'), function toggleWireframe(d3_event) {
97872 d3_event.preventDefault();
97873 d3_event.stopPropagation();
97874 context.map().toggleWireframe();
97875 }).on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData(d3_event) {
97876 d3_event.preventDefault();
97877 d3_event.stopPropagation(); // Don't allow layer changes while drawing - #6584
97879 var mode = context.mode();
97880 if (mode && /^draw/.test(mode.id)) return;
97881 var layer = context.layers().layer('osm');
97884 layer.enabled(!layer.enabled());
97886 if (!layer.enabled()) {
97887 context.enter(modeBrowse(context));
97890 }).on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited(d3_event) {
97891 d3_event.preventDefault();
97892 context.map().toggleHighlightEdited();
97894 context.on('enter.editor', function (entered) {
97895 container.classed('mode-' + entered.id, true);
97896 }).on('exit.editor', function (exited) {
97897 container.classed('mode-' + exited.id, false);
97899 context.enter(modeBrowse(context));
97901 if (!_initCounter++) {
97902 if (!ui.hash.startWalkthrough) {
97903 context.container().call(uiSplash(context)).call(uiRestore(context));
97906 context.container().call(ui.shortcuts);
97909 var osm = context.connection();
97910 var auth = uiLoading(context).message(_t.html('loading_auth')).blocking(true);
97913 osm.on('authLoading.ui', function () {
97914 context.container().call(auth);
97915 }).on('authDone.ui', function () {
97922 if (ui.hash.startWalkthrough) {
97923 ui.hash.startWalkthrough = false;
97924 context.container().call(uiIntro(context));
97928 return function (d3_event) {
97929 if (d3_event.shiftKey) return;
97930 if (context.container().select('.combobox').size()) return;
97931 d3_event.preventDefault();
97932 context.map().pan(d, 100);
97939 var _loadPromise; // renders the iD interface into the container node
97942 ui.ensureLoaded = function () {
97943 if (_loadPromise) return _loadPromise;
97944 return _loadPromise = Promise.all([// must have strings and presets before loading the UI
97945 _mainLocalizer.ensureLoaded(), _mainPresetIndex.ensureLoaded()]).then(function () {
97946 if (!context.container().empty()) render(context.container());
97947 })["catch"](function (err) {
97948 return console.error(err);
97949 }); // eslint-disable-line
97950 }; // `ui.restart()` will destroy and rebuild the entire iD interface,
97951 // for example to switch the locale while iD is running.
97954 ui.restart = function () {
97955 context.keybinding().clear();
97956 _loadPromise = null;
97957 context.container().selectAll('*').remove();
97961 ui.lastPointerType = function () {
97962 return _lastPointerType;
97965 ui.svgDefs = svgDefs(context);
97966 ui.flash = uiFlash(context);
97967 ui.sidebar = uiSidebar(context);
97968 ui.photoviewer = uiPhotoviewer(context);
97969 ui.shortcuts = uiShortcuts(context);
97971 ui.onResize = function (withPan) {
97972 var map = context.map(); // Recalc dimensions of map and sidebar.. (`true` = force recalc)
97973 // This will call `getBoundingClientRect` and trigger reflow,
97974 // but the values will be cached for later use.
97976 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
97977 utilGetDimensions(context.container().select('.sidebar'), true);
97979 if (withPan !== undefined) {
97980 map.redrawEnable(false);
97982 map.redrawEnable(true);
97985 map.dimensions(mapDimensions);
97986 ui.photoviewer.onMapResize(); // check if header or footer have overflowed
97988 ui.checkOverflow('.top-toolbar');
97989 ui.checkOverflow('.map-footer-bar'); // Use outdated code so it works on Explorer
97991 var resizeWindowEvent = document.createEvent('Event');
97992 resizeWindowEvent.initEvent('resizeWindow', true, true);
97993 document.dispatchEvent(resizeWindowEvent);
97994 }; // Call checkOverflow when resizing or whenever the contents change.
97997 ui.checkOverflow = function (selector, reset) {
97999 delete _needWidth[selector];
98002 var element = select(selector);
98003 var scrollWidth = element.property('scrollWidth');
98004 var clientWidth = element.property('clientWidth');
98005 var needed = _needWidth[selector] || scrollWidth;
98007 if (scrollWidth > clientWidth) {
98008 // overflow happening
98009 element.classed('narrow', true);
98011 if (!_needWidth[selector]) {
98012 _needWidth[selector] = scrollWidth;
98014 } else if (scrollWidth >= needed) {
98015 element.classed('narrow', false);
98019 ui.togglePanes = function (showPane) {
98020 var hidePanes = context.container().selectAll('.map-pane.shown');
98021 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
98022 hidePanes.classed('shown', false).classed('hide', true);
98023 context.container().selectAll('.map-pane-control button').classed('active', false);
98026 hidePanes.classed('shown', false).classed('hide', true).style(side, '-500px');
98027 context.container().selectAll('.' + showPane.attr('pane') + '-control button').classed('active', true);
98028 showPane.classed('shown', true).classed('hide', false);
98030 if (hidePanes.empty()) {
98031 showPane.style(side, '-500px').transition().duration(200).style(side, '0px');
98033 showPane.style(side, '0px');
98036 hidePanes.classed('shown', true).classed('hide', false).style(side, '0px').transition().duration(200).style(side, '-500px').on('end', function () {
98037 select(this).classed('shown', false).classed('hide', true);
98042 var _editMenu = uiEditMenu(context);
98044 ui.editMenu = function () {
98048 ui.showEditMenu = function (anchorPoint, triggerType, operations) {
98049 // remove any displayed menu
98050 ui.closeEditMenu();
98051 if (!operations && context.mode().operations) operations = context.mode().operations();
98052 if (!operations || !operations.length) return; // disable menu if in wide selection, for example
98054 if (!context.map().editableDataEnabled()) return;
98055 var surfaceNode = context.surface().node();
98057 if (surfaceNode.focus) {
98058 // FF doesn't support it
98059 // focus the surface or else clicking off the menu may not trigger modeBrowse
98060 surfaceNode.focus();
98063 operations.forEach(function (operation) {
98064 if (operation.point) operation.point(anchorPoint);
98067 _editMenu.anchorLoc(anchorPoint).triggerType(triggerType).operations(operations); // render the menu
98070 context.map().supersurface.call(_editMenu);
98073 ui.closeEditMenu = function () {
98074 // remove any existing menu no matter how it was added
98075 context.map().supersurface.select('.edit-menu').remove();
98078 var _saveLoading = select(null);
98080 context.uploader().on('saveStarted.ui', function () {
98081 _saveLoading = uiLoading(context).message(_t.html('save.uploading')).blocking(true);
98082 context.container().call(_saveLoading); // block input during upload
98083 }).on('saveEnded.ui', function () {
98084 _saveLoading.close();
98086 _saveLoading = select(null);
98091 function coreContext() {
98094 var dispatch$1 = dispatch('enter', 'exit', 'change');
98095 var context = utilRebind({}, dispatch$1, 'on');
98097 var _deferred = new Set();
98099 context.version = '2.19.2';
98100 context.privacyVersion = '20200407'; // iD will alter the hash so cache the parameters intended to setup the session
98102 context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
98103 context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
98105 // An osmChangeset object. Not loaded until needed.
98107 context.changeset = null;
98108 var _defaultChangesetComment = context.initialHashParams.comment;
98109 var _defaultChangesetSource = context.initialHashParams.source;
98110 var _defaultChangesetHashtags = context.initialHashParams.hashtags;
98112 context.defaultChangesetComment = function (val) {
98113 if (!arguments.length) return _defaultChangesetComment;
98114 _defaultChangesetComment = val;
98118 context.defaultChangesetSource = function (val) {
98119 if (!arguments.length) return _defaultChangesetSource;
98120 _defaultChangesetSource = val;
98124 context.defaultChangesetHashtags = function (val) {
98125 if (!arguments.length) return _defaultChangesetHashtags;
98126 _defaultChangesetHashtags = val;
98129 /* Document title */
98131 /* (typically shown as the label for the browser window/tab) */
98132 // If true, iD will update the title based on what the user is doing
98135 var _setsDocumentTitle = true;
98137 context.setsDocumentTitle = function (val) {
98138 if (!arguments.length) return _setsDocumentTitle;
98139 _setsDocumentTitle = val;
98141 }; // The part of the title that is always the same
98144 var _documentTitleBase = document.title;
98146 context.documentTitleBase = function (val) {
98147 if (!arguments.length) return _documentTitleBase;
98148 _documentTitleBase = val;
98151 /* User interface and keybinding */
98156 context.ui = function () {
98160 context.lastPointerType = function () {
98161 return _ui.lastPointerType();
98164 var _keybinding = utilKeybinding('context');
98166 context.keybinding = function () {
98167 return _keybinding;
98170 select(document).call(_keybinding);
98171 /* Straight accessors. Avoid using these if you can. */
98172 // Instantiate the connection here because it doesn't require passing in
98173 // `context` and it's needed for pre-init calls like `preauth`
98175 var _connection = services.osm;
98183 context.connection = function () {
98184 return _connection;
98187 context.history = function () {
98191 context.validator = function () {
98195 context.uploader = function () {
98201 context.preauth = function (options) {
98203 _connection["switch"](options);
98208 /* connection options for source switcher (optional) */
98211 var _apiConnections;
98213 context.apiConnections = function (val) {
98214 if (!arguments.length) return _apiConnections;
98215 _apiConnections = val;
98217 }; // A string or array or locale codes to prefer over the browser's settings
98220 context.locale = function (locale) {
98221 if (!arguments.length) return _mainLocalizer.localeCode();
98222 _mainLocalizer.preferredLocaleCodes(locale);
98226 function afterLoad(cid, callback) {
98227 return function (err, result) {
98229 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
98230 if (err.status === 400 || err.status === 401 || err.status === 403) {
98232 _connection.logout();
98236 if (typeof callback === 'function') {
98241 } else if (_connection && _connection.getConnectionId() !== cid) {
98242 if (typeof callback === 'function') {
98244 message: 'Connection Switched',
98251 _history.merge(result.data, result.extent);
98253 if (typeof callback === 'function') {
98254 callback(err, result);
98262 context.loadTiles = function (projection, callback) {
98263 var handle = window.requestIdleCallback(function () {
98264 _deferred["delete"](handle);
98266 if (_connection && context.editableDataEnabled()) {
98267 var cid = _connection.getConnectionId();
98269 _connection.loadTiles(projection, afterLoad(cid, callback));
98273 _deferred.add(handle);
98276 context.loadTileAtLoc = function (loc, callback) {
98277 var handle = window.requestIdleCallback(function () {
98278 _deferred["delete"](handle);
98280 if (_connection && context.editableDataEnabled()) {
98281 var cid = _connection.getConnectionId();
98283 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
98287 _deferred.add(handle);
98290 context.loadEntity = function (entityID, callback) {
98292 var cid = _connection.getConnectionId();
98294 _connection.loadEntity(entityID, afterLoad(cid, callback));
98298 context.zoomToEntity = function (entityID, zoomTo) {
98299 if (zoomTo !== false) {
98300 context.loadEntity(entityID, function (err, result) {
98302 var entity = result.data.find(function (e) {
98303 return e.id === entityID;
98307 _map.zoomTo(entity);
98312 _map.on('drawn.zoomToEntity', function () {
98313 if (!context.hasEntity(entityID)) return;
98315 _map.on('drawn.zoomToEntity', null);
98317 context.on('enter.zoomToEntity', null);
98318 context.enter(modeSelect(context, [entityID]));
98321 context.on('enter.zoomToEntity', function () {
98322 if (_mode.id !== 'browse') {
98323 _map.on('drawn.zoomToEntity', null);
98325 context.on('enter.zoomToEntity', null);
98330 var _minEditableZoom = 16;
98332 context.minEditableZoom = function (val) {
98333 if (!arguments.length) return _minEditableZoom;
98334 _minEditableZoom = val;
98337 _connection.tileZoom(val);
98341 }; // String length limits in Unicode characters, not JavaScript UTF-16 code units
98344 context.maxCharsForTagKey = function () {
98348 context.maxCharsForTagValue = function () {
98352 context.maxCharsForRelationRole = function () {
98356 function cleanOsmString(val, maxChars) {
98357 // be lenient with input
98358 if (val === undefined || val === null) {
98361 val = val.toString();
98362 } // remove whitespace
98365 val = val.trim(); // use the canonical form of the string
98367 if (val.normalize) val = val.normalize('NFC'); // trim to the number of allowed characters
98369 return utilUnicodeCharsTruncated(val, maxChars);
98372 context.cleanTagKey = function (val) {
98373 return cleanOsmString(val, context.maxCharsForTagKey());
98376 context.cleanTagValue = function (val) {
98377 return cleanOsmString(val, context.maxCharsForTagValue());
98380 context.cleanRelationRole = function (val) {
98381 return cleanOsmString(val, context.maxCharsForRelationRole());
98386 var _inIntro = false;
98388 context.inIntro = function (val) {
98389 if (!arguments.length) return _inIntro;
98392 }; // Immediately save the user's history to localstorage, if possible
98393 // This is called someteimes, but also on the `window.onbeforeunload` handler
98396 context.save = function () {
98397 // no history save, no message onbeforeunload
98398 if (_inIntro || context.container().select('.modal').size()) return;
98401 if (_mode && _mode.id === 'save') {
98402 canSave = false; // Attempt to prevent user from creating duplicate changes - see #5200
98404 if (services.osm && services.osm.isChangesetInflight()) {
98405 _history.clearSaved();
98410 canSave = context.selectedIDs().every(function (id) {
98411 var entity = context.hasEntity(id);
98412 return entity && !entity.isDegenerate();
98420 if (_history.hasChanges()) {
98421 return _t('save.unsaved_changes');
98423 }; // Debounce save, since it's a synchronous localStorage write,
98424 // and history changes can happen frequently (e.g. when dragging).
98427 context.debouncedSave = debounce(context.save, 350);
98429 function withDebouncedSave(fn) {
98430 return function () {
98431 var result = fn.apply(_history, arguments);
98432 context.debouncedSave();
98439 context.hasEntity = function (id) {
98440 return _history.graph().hasEntity(id);
98443 context.entity = function (id) {
98444 return _history.graph().entity(id);
98451 context.mode = function () {
98455 context.enter = function (newMode) {
98459 dispatch$1.call('exit', _this, _mode);
98466 dispatch$1.call('enter', _this, _mode);
98469 context.selectedIDs = function () {
98470 return _mode && _mode.selectedIDs && _mode.selectedIDs() || [];
98473 context.activeID = function () {
98474 return _mode && _mode.activeID && _mode.activeID();
98477 var _selectedNoteID;
98479 context.selectedNoteID = function (noteID) {
98480 if (!arguments.length) return _selectedNoteID;
98481 _selectedNoteID = noteID;
98483 }; // NOTE: Don't change the name of this until UI v3 is merged
98486 var _selectedErrorID;
98488 context.selectedErrorID = function (errorID) {
98489 if (!arguments.length) return _selectedErrorID;
98490 _selectedErrorID = errorID;
98496 context.install = function (behavior) {
98497 return context.surface().call(behavior);
98500 context.uninstall = function (behavior) {
98501 return context.surface().call(behavior.off);
98508 context.copyGraph = function () {
98514 context.copyIDs = function (val) {
98515 if (!arguments.length) return _copyIDs;
98517 _copyGraph = _history.graph();
98523 context.copyLonLat = function (val) {
98524 if (!arguments.length) return _copyLonLat;
98533 context.background = function () {
98534 return _background;
98541 context.features = function () {
98545 context.hasHiddenConnections = function (id) {
98546 var graph = _history.graph();
98548 var entity = graph.entity(id);
98549 return _features.hasHiddenConnections(entity, graph);
98556 context.photos = function () {
98564 context.map = function () {
98568 context.layers = function () {
98569 return _map.layers();
98572 context.surface = function () {
98573 return _map.surface;
98576 context.editableDataEnabled = function () {
98577 return _map.editableDataEnabled();
98580 context.surfaceRect = function () {
98581 return _map.surface.node().getBoundingClientRect();
98584 context.editable = function () {
98585 // don't allow editing during save
98586 var mode = context.mode();
98587 if (!mode || mode.id === 'save') return false;
98588 return _map.editableDataEnabled();
98593 var _debugFlags = {
98597 // label collision bounding boxes
98599 // imagery bounding polygons
98602 downloaded: false // downloaded data from osm
98606 context.debugFlags = function () {
98607 return _debugFlags;
98610 context.getDebug = function (flag) {
98611 return flag && _debugFlags[flag];
98614 context.setDebug = function (flag, val) {
98615 if (arguments.length === 1) val = true;
98616 _debugFlags[flag] = val;
98617 dispatch$1.call('change');
98623 var _container = select(null);
98625 context.container = function (val) {
98626 if (!arguments.length) return _container;
98629 _container.classed('ideditor', true);
98634 context.containerNode = function (val) {
98635 if (!arguments.length) return context.container().node();
98636 context.container(select(val));
98642 context.embed = function (val) {
98643 if (!arguments.length) return _embed;
98650 var _assetPath = '';
98652 context.assetPath = function (val) {
98653 if (!arguments.length) return _assetPath;
98655 _mainFileFetcher.assetPath(val);
98659 var _assetMap = {};
98661 context.assetMap = function (val) {
98662 if (!arguments.length) return _assetMap;
98664 _mainFileFetcher.assetMap(val);
98668 context.asset = function (val) {
98669 if (/^http(s)?:\/\//i.test(val)) return val;
98670 var filename = _assetPath + val;
98671 return _assetMap[filename] || filename;
98674 context.imagePath = function (val) {
98675 return context.asset("img/".concat(val));
98677 /* reset (aka flush) */
98680 context.reset = context.flush = function () {
98681 context.debouncedSave.cancel();
98682 Array.from(_deferred).forEach(function (handle) {
98683 window.cancelIdleCallback(handle);
98685 _deferred["delete"](handle);
98687 Object.values(services).forEach(function (service) {
98688 if (service && typeof service.reset === 'function') {
98689 service.reset(context);
98692 context.changeset = null;
98694 _validator.reset();
98700 _uploader.reset(); // don't leave stale state in the inspector
98703 context.container().select('.inspector-wrap *').remove();
98709 context.projection = geoRawMercator();
98710 context.curtainProjection = geoRawMercator();
98713 context.init = function () {
98714 instantiateInternal();
98715 initializeDependents();
98716 return context; // Load variables and properties. No property of `context` should be accessed
98717 // until this is complete since load statuses are indeterminate. The order
98718 // of instantiation shouldn't matter.
98720 function instantiateInternal() {
98721 _history = coreHistory(context);
98722 context.graph = _history.graph;
98723 context.pauseChangeDispatch = _history.pauseChangeDispatch;
98724 context.resumeChangeDispatch = _history.resumeChangeDispatch;
98725 context.perform = withDebouncedSave(_history.perform);
98726 context.replace = withDebouncedSave(_history.replace);
98727 context.pop = withDebouncedSave(_history.pop);
98728 context.overwrite = withDebouncedSave(_history.overwrite);
98729 context.undo = withDebouncedSave(_history.undo);
98730 context.redo = withDebouncedSave(_history.redo);
98731 _validator = coreValidator(context);
98732 _uploader = coreUploader(context);
98733 _background = rendererBackground(context);
98734 _features = rendererFeatures(context);
98735 _map = rendererMap(context);
98736 _photos = rendererPhotos(context);
98737 _ui = uiInit(context);
98738 } // Set up objects that might need to access properties of `context`. The order
98739 // might matter if dependents make calls to each other. Be wary of async calls.
98742 function initializeDependents() {
98743 if (context.initialHashParams.presets) {
98744 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
98747 if (context.initialHashParams.locale) {
98748 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
98749 } // kick off some async work
98752 _mainLocalizer.ensureLoaded();
98754 _background.ensureLoaded();
98756 _mainPresetIndex.ensureLoaded();
98757 Object.values(services).forEach(function (service) {
98758 if (service && typeof service.init === 'function') {
98769 if (services.maprules && context.initialHashParams.maprules) {
98770 d3_json(context.initialHashParams.maprules).then(function (mapcss) {
98771 services.maprules.init();
98772 mapcss.forEach(function (mapcssSelector) {
98773 return services.maprules.addRule(mapcssSelector);
98775 })["catch"](function () {
98778 } // if the container isn't available, e.g. when testing, don't load the UI
98781 if (!context.container().empty()) {
98782 _ui.ensureLoaded().then(function () {
98792 // This is only done in testing because of the performance penalty.
98794 var debug = false; // Reexport just what our tests use, see #4379
98796 dispatch: dispatch,
98797 geoMercator: mercator,
98798 geoProjection: projection,
98799 polygonArea: d3_polygonArea,
98800 polygonCentroid: d3_polygonCentroid,
98802 selectAll: selectAll,
98803 timerFlush: timerFlush
98806 var iD = /*#__PURE__*/Object.freeze({
98810 actionAddEntity: actionAddEntity,
98811 actionAddMember: actionAddMember,
98812 actionAddMidpoint: actionAddMidpoint,
98813 actionAddVertex: actionAddVertex,
98814 actionChangeMember: actionChangeMember,
98815 actionChangePreset: actionChangePreset,
98816 actionChangeTags: actionChangeTags,
98817 actionCircularize: actionCircularize,
98818 actionConnect: actionConnect,
98819 actionCopyEntities: actionCopyEntities,
98820 actionDeleteMember: actionDeleteMember,
98821 actionDeleteMultiple: actionDeleteMultiple,
98822 actionDeleteNode: actionDeleteNode,
98823 actionDeleteRelation: actionDeleteRelation,
98824 actionDeleteWay: actionDeleteWay,
98825 actionDiscardTags: actionDiscardTags,
98826 actionDisconnect: actionDisconnect,
98827 actionExtract: actionExtract,
98828 actionJoin: actionJoin,
98829 actionMerge: actionMerge,
98830 actionMergeNodes: actionMergeNodes,
98831 actionMergePolygon: actionMergePolygon,
98832 actionMergeRemoteChanges: actionMergeRemoteChanges,
98833 actionMove: actionMove,
98834 actionMoveMember: actionMoveMember,
98835 actionMoveNode: actionMoveNode,
98836 actionNoop: actionNoop,
98837 actionOrthogonalize: actionOrthogonalize,
98838 actionRestrictTurn: actionRestrictTurn,
98839 actionReverse: actionReverse,
98840 actionRevert: actionRevert,
98841 actionRotate: actionRotate,
98842 actionScale: actionScale,
98843 actionSplit: actionSplit,
98844 actionStraightenNodes: actionStraightenNodes,
98845 actionStraightenWay: actionStraightenWay,
98846 actionUnrestrictTurn: actionUnrestrictTurn,
98847 actionReflect: actionReflect,
98848 actionUpgradeTags: actionUpgradeTags,
98849 behaviorAddWay: behaviorAddWay,
98850 behaviorBreathe: behaviorBreathe,
98851 behaviorDrag: behaviorDrag,
98852 behaviorDrawWay: behaviorDrawWay,
98853 behaviorDraw: behaviorDraw,
98854 behaviorEdit: behaviorEdit,
98855 behaviorHash: behaviorHash,
98856 behaviorHover: behaviorHover,
98857 behaviorLasso: behaviorLasso,
98858 behaviorOperation: behaviorOperation,
98859 behaviorPaste: behaviorPaste,
98860 behaviorSelect: behaviorSelect,
98861 coreContext: coreContext,
98862 coreFileFetcher: coreFileFetcher,
98863 fileFetcher: _mainFileFetcher,
98864 coreDifference: coreDifference,
98865 coreGraph: coreGraph,
98866 coreHistory: coreHistory,
98867 coreLocalizer: coreLocalizer,
98869 localizer: _mainLocalizer,
98870 prefs: corePreferences,
98871 coreTree: coreTree,
98872 coreUploader: coreUploader,
98873 coreValidator: coreValidator,
98874 geoExtent: geoExtent,
98875 geoLatToMeters: geoLatToMeters,
98876 geoLonToMeters: geoLonToMeters,
98877 geoMetersToLat: geoMetersToLat,
98878 geoMetersToLon: geoMetersToLon,
98879 geoMetersToOffset: geoMetersToOffset,
98880 geoOffsetToMeters: geoOffsetToMeters,
98881 geoScaleToZoom: geoScaleToZoom,
98882 geoSphericalClosestNode: geoSphericalClosestNode,
98883 geoSphericalDistance: geoSphericalDistance,
98884 geoZoomToScale: geoZoomToScale,
98885 geoAngle: geoAngle,
98886 geoChooseEdge: geoChooseEdge,
98887 geoEdgeEqual: geoEdgeEqual,
98888 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
98889 geoHasLineIntersections: geoHasLineIntersections,
98890 geoHasSelfIntersections: geoHasSelfIntersections,
98891 geoRotate: geoRotate,
98892 geoLineIntersection: geoLineIntersection,
98893 geoPathHasIntersections: geoPathHasIntersections,
98894 geoPathIntersections: geoPathIntersections,
98895 geoPathLength: geoPathLength,
98896 geoPointInPolygon: geoPointInPolygon,
98897 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
98898 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
98899 geoViewportEdge: geoViewportEdge,
98900 geoRawMercator: geoRawMercator,
98901 geoVecAdd: geoVecAdd,
98902 geoVecAngle: geoVecAngle,
98903 geoVecCross: geoVecCross,
98904 geoVecDot: geoVecDot,
98905 geoVecEqual: geoVecEqual,
98906 geoVecFloor: geoVecFloor,
98907 geoVecInterp: geoVecInterp,
98908 geoVecLength: geoVecLength,
98909 geoVecLengthSquare: geoVecLengthSquare,
98910 geoVecNormalize: geoVecNormalize,
98911 geoVecNormalizedDot: geoVecNormalizedDot,
98912 geoVecProject: geoVecProject,
98913 geoVecSubtract: geoVecSubtract,
98914 geoVecScale: geoVecScale,
98915 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
98916 geoOrthoCalcScore: geoOrthoCalcScore,
98917 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
98918 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
98919 modeAddArea: modeAddArea,
98920 modeAddLine: modeAddLine,
98921 modeAddPoint: modeAddPoint,
98922 modeAddNote: modeAddNote,
98923 modeBrowse: modeBrowse,
98924 modeDragNode: modeDragNode,
98925 modeDragNote: modeDragNote,
98926 modeDrawArea: modeDrawArea,
98927 modeDrawLine: modeDrawLine,
98928 modeMove: modeMove,
98929 modeRotate: modeRotate,
98930 modeSave: modeSave,
98931 modeSelect: modeSelect,
98932 modeSelectData: modeSelectData,
98933 modeSelectError: modeSelectError,
98934 modeSelectNote: modeSelectNote,
98935 operationCircularize: operationCircularize,
98936 operationContinue: operationContinue,
98937 operationCopy: operationCopy,
98938 operationDelete: operationDelete,
98939 operationDisconnect: operationDisconnect,
98940 operationDowngrade: operationDowngrade,
98941 operationExtract: operationExtract,
98942 operationMerge: operationMerge,
98943 operationMove: operationMove,
98944 operationOrthogonalize: operationOrthogonalize,
98945 operationPaste: operationPaste,
98946 operationReflectShort: operationReflectShort,
98947 operationReflectLong: operationReflectLong,
98948 operationReverse: operationReverse,
98949 operationRotate: operationRotate,
98950 operationSplit: operationSplit,
98951 operationStraighten: operationStraighten,
98952 osmChangeset: osmChangeset,
98953 osmEntity: osmEntity,
98956 osmRelation: osmRelation,
98959 osmIntersection: osmIntersection,
98961 osmInferRestriction: osmInferRestriction,
98962 osmLanes: osmLanes,
98963 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
98964 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
98965 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
98966 osmJoinWays: osmJoinWays,
98967 get osmAreaKeys () { return osmAreaKeys; },
98968 osmSetAreaKeys: osmSetAreaKeys,
98969 osmTagSuggestingArea: osmTagSuggestingArea,
98970 get osmPointTags () { return osmPointTags; },
98971 osmSetPointTags: osmSetPointTags,
98972 get osmVertexTags () { return osmVertexTags; },
98973 osmSetVertexTags: osmSetVertexTags,
98974 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
98975 osmOneWayTags: osmOneWayTags,
98976 osmPavedTags: osmPavedTags,
98977 osmIsInterestingTag: osmIsInterestingTag,
98978 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
98979 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
98980 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
98981 presetCategory: presetCategory,
98982 presetCollection: presetCollection,
98983 presetField: presetField,
98984 presetPreset: presetPreset,
98985 presetManager: _mainPresetIndex,
98986 presetIndex: presetIndex,
98987 rendererBackgroundSource: rendererBackgroundSource,
98988 rendererBackground: rendererBackground,
98989 rendererFeatures: rendererFeatures,
98990 rendererMap: rendererMap,
98991 rendererPhotos: rendererPhotos,
98992 rendererTileLayer: rendererTileLayer,
98993 services: services,
98994 serviceKeepRight: serviceKeepRight,
98995 serviceImproveOSM: serviceImproveOSM,
98996 serviceOsmose: serviceOsmose,
98997 serviceMapillary: serviceMapillary,
98998 serviceMapRules: serviceMapRules,
98999 serviceNominatim: serviceNominatim,
99000 serviceOpenstreetcam: serviceOpenstreetcam,
99001 serviceOsm: serviceOsm,
99002 serviceOsmWikibase: serviceOsmWikibase,
99003 serviceStreetside: serviceStreetside,
99004 serviceTaginfo: serviceTaginfo,
99005 serviceVectorTile: serviceVectorTile,
99006 serviceWikidata: serviceWikidata,
99007 serviceWikipedia: serviceWikipedia,
99008 svgAreas: svgAreas,
99010 svgDebug: svgDebug,
99012 svgKeepRight: svgKeepRight,
99014 svgGeolocate: svgGeolocate,
99015 svgLabels: svgLabels,
99016 svgLayers: svgLayers,
99017 svgLines: svgLines,
99018 svgMapillaryImages: svgMapillaryImages,
99019 svgMapillarySigns: svgMapillarySigns,
99020 svgMidpoints: svgMidpoints,
99021 svgNotes: svgNotes,
99022 svgMarkerSegments: svgMarkerSegments,
99023 svgOpenstreetcamImages: svgOpenstreetcamImages,
99025 svgPassiveVertex: svgPassiveVertex,
99027 svgPointTransform: svgPointTransform,
99028 svgPoints: svgPoints,
99029 svgRelationMemberTags: svgRelationMemberTags,
99030 svgSegmentWay: svgSegmentWay,
99031 svgStreetside: svgStreetside,
99032 svgTagClasses: svgTagClasses,
99033 svgTagPattern: svgTagPattern,
99034 svgTouch: svgTouch,
99035 svgTurns: svgTurns,
99036 svgVertices: svgVertices,
99037 uiFieldDefaultCheck: uiFieldCheck,
99038 uiFieldOnewayCheck: uiFieldCheck,
99039 uiFieldCheck: uiFieldCheck,
99040 uiFieldManyCombo: uiFieldCombo,
99041 uiFieldMultiCombo: uiFieldCombo,
99042 uiFieldNetworkCombo: uiFieldCombo,
99043 uiFieldSemiCombo: uiFieldCombo,
99044 uiFieldTypeCombo: uiFieldCombo,
99045 uiFieldCombo: uiFieldCombo,
99046 uiFieldUrl: uiFieldText,
99047 uiFieldIdentifier: uiFieldText,
99048 uiFieldNumber: uiFieldText,
99049 uiFieldTel: uiFieldText,
99050 uiFieldEmail: uiFieldText,
99051 uiFieldText: uiFieldText,
99052 uiFieldAccess: uiFieldAccess,
99053 uiFieldAddress: uiFieldAddress,
99054 uiFieldCycleway: uiFieldCycleway,
99055 uiFieldLanes: uiFieldLanes,
99056 uiFieldLocalized: uiFieldLocalized,
99057 uiFieldMaxspeed: uiFieldMaxspeed,
99058 uiFieldStructureRadio: uiFieldRadio,
99059 uiFieldRadio: uiFieldRadio,
99060 uiFieldRestrictions: uiFieldRestrictions,
99061 uiFieldTextarea: uiFieldTextarea,
99062 uiFieldWikidata: uiFieldWikidata,
99063 uiFieldWikipedia: uiFieldWikipedia,
99064 uiFields: uiFields,
99066 uiPanelBackground: uiPanelBackground,
99067 uiPanelHistory: uiPanelHistory,
99068 uiPanelLocation: uiPanelLocation,
99069 uiPanelMeasurement: uiPanelMeasurement,
99070 uiInfoPanels: uiInfoPanels,
99071 uiPaneBackground: uiPaneBackground,
99072 uiPaneHelp: uiPaneHelp,
99073 uiPaneIssues: uiPaneIssues,
99074 uiPaneMapData: uiPaneMapData,
99075 uiPanePreferences: uiPanePreferences,
99076 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
99077 uiSectionBackgroundList: uiSectionBackgroundList,
99078 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
99079 uiSectionChanges: uiSectionChanges,
99080 uiSectionDataLayers: uiSectionDataLayers,
99081 uiSectionEntityIssues: uiSectionEntityIssues,
99082 uiSectionFeatureType: uiSectionFeatureType,
99083 uiSectionMapFeatures: uiSectionMapFeatures,
99084 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
99085 uiSectionOverlayList: uiSectionOverlayList,
99086 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
99087 uiSectionPresetFields: uiSectionPresetFields,
99088 uiSectionPrivacy: uiSectionPrivacy,
99089 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
99090 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
99091 uiSectionRawTagEditor: uiSectionRawTagEditor,
99092 uiSectionSelectionList: uiSectionSelectionList,
99093 uiSectionValidationIssues: uiSectionValidationIssues,
99094 uiSectionValidationOptions: uiSectionValidationOptions,
99095 uiSectionValidationRules: uiSectionValidationRules,
99096 uiSectionValidationStatus: uiSectionValidationStatus,
99097 uiSettingsCustomBackground: uiSettingsCustomBackground,
99098 uiSettingsCustomData: uiSettingsCustomData,
99100 uiAccount: uiAccount,
99101 uiAttribution: uiAttribution,
99102 uiChangesetEditor: uiChangesetEditor,
99104 uiCombobox: uiCombobox,
99105 uiCommit: uiCommit,
99106 uiCommitWarnings: uiCommitWarnings,
99107 uiConfirm: uiConfirm,
99108 uiConflicts: uiConflicts,
99109 uiContributors: uiContributors,
99110 uiCurtain: uiCurtain,
99111 uiDataEditor: uiDataEditor,
99112 uiDataHeader: uiDataHeader,
99113 uiDisclosure: uiDisclosure,
99114 uiEditMenu: uiEditMenu,
99115 uiEntityEditor: uiEntityEditor,
99116 uiFeatureInfo: uiFeatureInfo,
99117 uiFeatureList: uiFeatureList,
99119 uiFieldHelp: uiFieldHelp,
99121 uiFormFields: uiFormFields,
99122 uiFullScreen: uiFullScreen,
99123 uiGeolocate: uiGeolocate,
99124 uiImproveOsmComments: uiImproveOsmComments,
99125 uiImproveOsmDetails: uiImproveOsmDetails,
99126 uiImproveOsmEditor: uiImproveOsmEditor,
99127 uiImproveOsmHeader: uiImproveOsmHeader,
99129 uiInspector: uiInspector,
99130 uiIssuesInfo: uiIssuesInfo,
99131 uiKeepRightDetails: uiKeepRightDetails,
99132 uiKeepRightEditor: uiKeepRightEditor,
99133 uiKeepRightHeader: uiKeepRightHeader,
99135 uiLoading: uiLoading,
99136 uiMapInMap: uiMapInMap,
99138 uiNotice: uiNotice,
99139 uiNoteComments: uiNoteComments,
99140 uiNoteEditor: uiNoteEditor,
99141 uiNoteHeader: uiNoteHeader,
99142 uiNoteReport: uiNoteReport,
99143 uiPopover: uiPopover,
99144 uiPresetIcon: uiPresetIcon,
99145 uiPresetList: uiPresetList,
99146 uiRestore: uiRestore,
99148 uiSidebar: uiSidebar,
99149 uiSourceSwitch: uiSourceSwitch,
99150 uiSpinner: uiSpinner,
99151 uiSplash: uiSplash,
99152 uiStatus: uiStatus,
99153 uiSuccess: uiSuccess,
99154 uiTagReference: uiTagReference,
99155 uiToggle: uiToggle,
99156 uiTooltip: uiTooltip,
99157 uiVersion: uiVersion,
99158 uiViewOnOSM: uiViewOnOSM,
99159 uiViewOnKeepRight: uiViewOnKeepRight,
99161 utilAesEncrypt: utilAesEncrypt,
99162 utilAesDecrypt: utilAesDecrypt,
99163 utilArrayChunk: utilArrayChunk,
99164 utilArrayDifference: utilArrayDifference,
99165 utilArrayFlatten: utilArrayFlatten,
99166 utilArrayGroupBy: utilArrayGroupBy,
99167 utilArrayIdentical: utilArrayIdentical,
99168 utilArrayIntersection: utilArrayIntersection,
99169 utilArrayUnion: utilArrayUnion,
99170 utilArrayUniq: utilArrayUniq,
99171 utilArrayUniqBy: utilArrayUniqBy,
99172 utilAsyncMap: utilAsyncMap,
99173 utilCleanTags: utilCleanTags,
99174 utilCombinedTags: utilCombinedTags,
99175 utilDeepMemberSelector: utilDeepMemberSelector,
99176 utilDetect: utilDetect,
99177 utilDisplayName: utilDisplayName,
99178 utilDisplayNameForPath: utilDisplayNameForPath,
99179 utilDisplayType: utilDisplayType,
99180 utilDisplayLabel: utilDisplayLabel,
99181 utilEntityRoot: utilEntityRoot,
99182 utilEditDistance: utilEditDistance,
99183 utilEntitySelector: utilEntitySelector,
99184 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
99185 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
99186 utilFastMouse: utilFastMouse,
99187 utilFunctor: utilFunctor,
99188 utilGetAllNodes: utilGetAllNodes,
99189 utilGetSetValue: utilGetSetValue,
99190 utilHashcode: utilHashcode,
99191 utilHighlightEntities: utilHighlightEntities,
99192 utilKeybinding: utilKeybinding,
99193 utilNoAuto: utilNoAuto,
99194 utilObjectOmit: utilObjectOmit,
99195 utilPrefixCSSProperty: utilPrefixCSSProperty,
99196 utilPrefixDOMProperty: utilPrefixDOMProperty,
99197 utilQsString: utilQsString,
99198 utilRebind: utilRebind,
99199 utilSafeClassName: utilSafeClassName,
99200 utilSetTransform: utilSetTransform,
99201 utilSessionMutex: utilSessionMutex,
99202 utilStringQs: utilStringQs,
99203 utilTagDiff: utilTagDiff,
99204 utilTagText: utilTagText,
99205 utilTiler: utilTiler,
99206 utilTotalExtent: utilTotalExtent,
99207 utilTriggerEvent: utilTriggerEvent,
99208 utilUnicodeCharsCount: utilUnicodeCharsCount,
99209 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
99210 utilUniqueDomId: utilUniqueDomId,
99211 utilWrap: utilWrap,
99212 validationAlmostJunction: validationAlmostJunction,
99213 validationCloseNodes: validationCloseNodes,
99214 validationCrossingWays: validationCrossingWays,
99215 validationDisconnectedWay: validationDisconnectedWay,
99216 validationFormatting: validationFormatting,
99217 validationHelpRequest: validationHelpRequest,
99218 validationImpossibleOneway: validationImpossibleOneway,
99219 validationIncompatibleSource: validationIncompatibleSource,
99220 validationMaprules: validationMaprules,
99221 validationMismatchedGeometry: validationMismatchedGeometry,
99222 validationMissingRole: validationMissingRole,
99223 validationMissingTag: validationMissingTag,
99224 validationOutdatedTags: validationOutdatedTags,
99225 validationPrivateData: validationPrivateData,
99226 validationSuspiciousName: validationSuspiciousName,
99227 validationUnsquareWay: validationUnsquareWay
99230 window.requestIdleCallback = window.requestIdleCallback || function (cb) {
99231 var start = Date.now();
99232 return window.requestAnimationFrame(function () {
99235 timeRemaining: function timeRemaining() {
99236 return Math.max(0, 50 - (Date.now() - start));
99242 window.cancelIdleCallback = window.cancelIdleCallback || function (id) {
99243 window.cancelAnimationFrame(id);