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 var geojsonRewind = rewind;
26341 function rewind(gj, outer) {
26342 var type = gj && gj.type,
26345 if (type === 'FeatureCollection') {
26346 for (i = 0; i < gj.features.length; i++) {
26347 rewind(gj.features[i], outer);
26349 } else if (type === 'GeometryCollection') {
26350 for (i = 0; i < gj.geometries.length; i++) {
26351 rewind(gj.geometries[i], outer);
26353 } else if (type === 'Feature') {
26354 rewind(gj.geometry, outer);
26355 } else if (type === 'Polygon') {
26356 rewindRings(gj.coordinates, outer);
26357 } else if (type === 'MultiPolygon') {
26358 for (i = 0; i < gj.coordinates.length; i++) {
26359 rewindRings(gj.coordinates[i], outer);
26366 function rewindRings(rings, outer) {
26367 if (rings.length === 0) return;
26368 rewindRing(rings[0], outer);
26370 for (var i = 1; i < rings.length; i++) {
26371 rewindRing(rings[i], !outer);
26375 function rewindRing(ring, dir) {
26378 for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
26379 area += (ring[i][0] - ring[j][0]) * (ring[j][1] + ring[i][1]);
26382 if (area >= 0 !== !!dir) ring.reverse();
26385 function actionExtract(entityID) {
26386 var extractedNodeID;
26388 var action = function action(graph) {
26389 var entity = graph.entity(entityID);
26391 if (entity.type === 'node') {
26392 return extractFromNode(entity, graph);
26395 return extractFromWayOrRelation(entity, graph);
26398 function extractFromNode(node, graph) {
26399 extractedNodeID = node.id; // Create a new node to replace the one we will detach
26401 var replacement = osmNode({
26404 graph = graph.replace(replacement); // Process each way in turn, updating the graph as we go
26406 graph = graph.parentWays(node).reduce(function (accGraph, parentWay) {
26407 return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
26408 }, graph); // Process any relations too
26410 return graph.parentRelations(node).reduce(function (accGraph, parentRel) {
26411 return accGraph.replace(parentRel.replaceMember(node, replacement));
26415 function extractFromWayOrRelation(entity, graph) {
26416 var fromGeometry = entity.geometry(graph);
26417 var keysToCopyAndRetain = ['source', 'wheelchair'];
26418 var keysToRetain = ['area'];
26419 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer']; // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
26421 var extractedLoc = d3_geoCentroid(geojsonRewind(Object.assign({}, entity.asGeoJSON(graph)), true));
26423 if (!extractedLoc || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
26424 extractedLoc = entity.extent(graph).center();
26427 var indoorAreaValues = {
26434 var isBuilding = entity.tags.building && entity.tags.building !== 'no' || entity.tags['building:part'] && entity.tags['building:part'] !== 'no';
26435 var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
26436 var entityTags = Object.assign({}, entity.tags); // shallow copy
26438 var pointTags = {};
26440 for (var key in entityTags) {
26441 if (entity.type === 'relation' && key === 'type') {
26445 if (keysToRetain.indexOf(key) !== -1) {
26450 // don't transfer building-related tags
26451 if (buildingKeysToRetain.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
26452 } // leave `indoor` tag on the area
26455 if (isIndoorArea && key === 'indoor') {
26457 } // copy the tag from the entity to the point
26460 pointTags[key] = entityTags[key]; // leave addresses and some other tags so they're on both features
26462 if (keysToCopyAndRetain.indexOf(key) !== -1 || key.match(/^addr:.{1,}/)) {
26464 } else if (isIndoorArea && key === 'level') {
26465 // leave `level` on both features
26467 } // remove the tag from the entity
26470 delete entityTags[key];
26473 if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
26474 // ensure that areas keep area geometry
26475 entityTags.area = 'yes';
26478 var replacement = osmNode({
26482 graph = graph.replace(replacement);
26483 extractedNodeID = replacement.id;
26484 return graph.replace(entity.update({
26489 action.getExtractedNodeID = function () {
26490 return extractedNodeID;
26497 // This is the inverse of `iD.actionSplit`.
26500 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
26501 // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
26504 function actionJoin(ids) {
26505 function groupEntitiesByGeometry(graph) {
26506 var entities = ids.map(function (id) {
26507 return graph.entity(id);
26509 return Object.assign({
26511 }, utilArrayGroupBy(entities, function (entity) {
26512 return entity.geometry(graph);
26516 var action = function action(graph) {
26517 var ways = ids.map(graph.entity, graph);
26518 var survivorID = ways[0].id; // if any of the ways are sided (e.g. coastline, cliff, kerb)
26519 // sort them first so they establish the overall order - #6033
26521 ways.sort(function (a, b) {
26522 var aSided = a.isSided();
26523 var bSided = b.isSided();
26524 return aSided && !bSided ? -1 : bSided && !aSided ? 1 : 0;
26525 }); // Prefer to keep an existing way.
26527 for (var i = 0; i < ways.length; i++) {
26528 if (!ways[i].isNew()) {
26529 survivorID = ways[i].id;
26534 var sequences = osmJoinWays(ways, graph);
26535 var joined = sequences[0]; // We might need to reverse some of these ways before joining them. #4688
26536 // `joined.actions` property will contain any actions we need to apply.
26538 graph = sequences.actions.reduce(function (g, action) {
26541 var survivor = graph.entity(survivorID);
26542 survivor = survivor.update({
26543 nodes: joined.nodes.map(function (n) {
26547 graph = graph.replace(survivor);
26548 joined.forEach(function (way) {
26549 if (way.id === survivorID) return;
26550 graph.parentRelations(way).forEach(function (parent) {
26551 graph = graph.replace(parent.replaceMember(way, survivor));
26553 survivor = survivor.mergeTags(way.tags);
26554 graph = graph.replace(survivor);
26555 graph = actionDeleteWay(way.id)(graph);
26556 }); // Finds if the join created a single-member multipolygon,
26557 // and if so turns it into a basic area instead
26559 function checkForSimpleMultipolygon() {
26560 if (!survivor.isClosed()) return;
26561 var multipolygons = graph.parentMultipolygons(survivor).filter(function (multipolygon) {
26562 // find multipolygons where the survivor is the only member
26563 return multipolygon.members.length === 1;
26564 }); // skip if this is the single member of multiple multipolygons
26566 if (multipolygons.length !== 1) return;
26567 var multipolygon = multipolygons[0];
26569 for (var key in survivor.tags) {
26570 if (multipolygon.tags[key] && // don't collapse if tags cannot be cleanly merged
26571 multipolygon.tags[key] !== survivor.tags[key]) return;
26574 survivor = survivor.mergeTags(multipolygon.tags);
26575 graph = graph.replace(survivor);
26576 graph = actionDeleteRelation(multipolygon.id, true
26577 /* allow untagged members */
26579 var tags = Object.assign({}, survivor.tags);
26581 if (survivor.geometry(graph) !== 'area') {
26582 // ensure the feature persists as an area
26586 delete tags.type; // remove type=multipolygon
26588 survivor = survivor.update({
26591 graph = graph.replace(survivor);
26594 checkForSimpleMultipolygon();
26596 }; // Returns the number of nodes the resultant way is expected to have
26599 action.resultingWayNodesLength = function (graph) {
26600 return ids.reduce(function (count, id) {
26601 return count + graph.entity(id).nodes.length;
26602 }, 0) - ids.length - 1;
26605 action.disabled = function (graph) {
26606 var geometries = groupEntitiesByGeometry(graph);
26608 if (ids.length < 2 || ids.length !== geometries.line.length) {
26609 return 'not_eligible';
26612 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
26614 if (joined.length > 1) {
26615 return 'not_adjacent';
26616 } // Loop through all combinations of path-pairs
26617 // to check potential intersections between all pairs
26620 for (var i = 0; i < ids.length - 1; i++) {
26621 for (var j = i + 1; j < ids.length; j++) {
26622 var path1 = graph.childNodes(graph.entity(ids[i])).map(function (e) {
26625 var path2 = graph.childNodes(graph.entity(ids[j])).map(function (e) {
26628 var intersections = geoPathIntersections(path1, path2); // Check if intersections are just nodes lying on top of
26629 // each other/the line, as opposed to crossing it
26631 var common = utilArrayIntersection(joined[0].nodes.map(function (n) {
26632 return n.loc.toString();
26633 }), intersections.map(function (n) {
26634 return n.toString();
26637 if (common.length !== intersections.length) {
26638 return 'paths_intersect';
26643 var nodeIds = joined[0].nodes.map(function (n) {
26648 var conflicting = false;
26649 joined[0].forEach(function (way) {
26650 var parents = graph.parentRelations(way);
26651 parents.forEach(function (parent) {
26652 if (parent.isRestriction() && parent.members.some(function (m) {
26653 return nodeIds.indexOf(m.id) >= 0;
26659 for (var k in way.tags) {
26660 if (!(k in tags)) {
26661 tags[k] = way.tags[k];
26662 } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
26663 conflicting = true;
26669 return 'restriction';
26673 return 'conflicting_tags';
26680 function actionMerge(ids) {
26681 function groupEntitiesByGeometry(graph) {
26682 var entities = ids.map(function (id) {
26683 return graph.entity(id);
26685 return Object.assign({
26690 }, utilArrayGroupBy(entities, function (entity) {
26691 return entity.geometry(graph);
26695 var action = function action(graph) {
26696 var geometries = groupEntitiesByGeometry(graph);
26697 var target = geometries.area[0] || geometries.line[0];
26698 var points = geometries.point;
26699 points.forEach(function (point) {
26700 target = target.mergeTags(point.tags);
26701 graph = graph.replace(target);
26702 graph.parentRelations(point).forEach(function (parent) {
26703 graph = graph.replace(parent.replaceMember(point, target));
26705 var nodes = utilArrayUniq(graph.childNodes(target));
26706 var removeNode = point;
26708 for (var i = 0; i < nodes.length; i++) {
26709 var node = nodes[i];
26711 if (graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags()) {
26713 } // Found an uninteresting child node on the target way.
26714 // Move orig point into its place to preserve point's history. #3683
26717 graph = graph.replace(point.update({
26721 target = target.replaceNode(node.id, point.id);
26722 graph = graph.replace(target);
26727 graph = graph.remove(removeNode);
26730 if (target.tags.area === 'yes') {
26731 var tags = Object.assign({}, target.tags); // shallow copy
26735 if (osmTagSuggestingArea(tags)) {
26736 // remove the `area` tag if area geometry is now implied - #3851
26737 target = target.update({
26740 graph = graph.replace(target);
26747 action.disabled = function (graph) {
26748 var geometries = groupEntitiesByGeometry(graph);
26750 if (geometries.point.length === 0 || geometries.area.length + geometries.line.length !== 1 || geometries.relation.length !== 0) {
26751 return 'not_eligible';
26759 // 1. move all the nodes to a common location
26760 // 2. `actionConnect` them
26762 function actionMergeNodes(nodeIDs, loc) {
26763 // If there is a single "interesting" node, use that as the location.
26764 // Otherwise return the average location of all the nodes.
26765 function chooseLoc(graph) {
26766 if (!nodeIDs.length) return null;
26768 var interestingCount = 0;
26769 var interestingLoc;
26771 for (var i = 0; i < nodeIDs.length; i++) {
26772 var node = graph.entity(nodeIDs[i]);
26774 if (node.hasInterestingTags()) {
26775 interestingLoc = ++interestingCount === 1 ? node.loc : null;
26778 sum = geoVecAdd(sum, node.loc);
26781 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
26784 var action = function action(graph) {
26785 if (nodeIDs.length < 2) return graph;
26789 toLoc = chooseLoc(graph);
26792 for (var i = 0; i < nodeIDs.length; i++) {
26793 var node = graph.entity(nodeIDs[i]);
26795 if (node.loc !== toLoc) {
26796 graph = graph.replace(node.move(toLoc));
26800 return actionConnect(nodeIDs)(graph);
26803 action.disabled = function (graph) {
26804 if (nodeIDs.length < 2) return 'not_eligible';
26806 for (var i = 0; i < nodeIDs.length; i++) {
26807 var entity = graph.entity(nodeIDs[i]);
26808 if (entity.type !== 'node') return 'not_eligible';
26811 return actionConnect(nodeIDs).disabled(graph);
26817 function osmChangeset() {
26818 if (!(this instanceof osmChangeset)) {
26819 return new osmChangeset().initialize(arguments);
26820 } else if (arguments.length) {
26821 this.initialize(arguments);
26824 osmEntity.changeset = osmChangeset;
26825 osmChangeset.prototype = Object.create(osmEntity.prototype);
26826 Object.assign(osmChangeset.prototype, {
26828 extent: function extent() {
26829 return new geoExtent();
26831 geometry: function geometry() {
26832 return 'changeset';
26834 asJXON: function asJXON() {
26838 tag: Object.keys(this.tags).map(function (k) {
26850 // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
26851 // XML. Returns a string.
26852 osmChangeJXON: function osmChangeJXON(changes) {
26853 var changeset_id = this.id;
26855 function nest(x, order) {
26858 for (var i = 0; i < x.length; i++) {
26859 var tagName = Object.keys(x[i])[0];
26860 if (!groups[tagName]) groups[tagName] = [];
26861 groups[tagName].push(x[i][tagName]);
26865 order.forEach(function (o) {
26866 if (groups[o]) ordered[o] = groups[o];
26869 } // sort relations in a changeset by dependencies
26872 function sort(changes) {
26873 // find a referenced relation in the current changeset
26874 function resolve(item) {
26875 return relations.find(function (relation) {
26876 return item.keyAttributes.type === 'relation' && item.keyAttributes.ref === relation['@id'];
26878 } // a new item is an item that has not been already processed
26881 function isNew(item) {
26882 return !sorted[item['@id']] && !processing.find(function (proc) {
26883 return proc['@id'] === item['@id'];
26887 var processing = [];
26889 var relations = changes.relation;
26890 if (!relations) return changes;
26892 for (var i = 0; i < relations.length; i++) {
26893 var relation = relations[i]; // skip relation if already sorted
26895 if (!sorted[relation['@id']]) {
26896 processing.push(relation);
26899 while (processing.length > 0) {
26900 var next = processing[0],
26901 deps = next.member.map(resolve).filter(Boolean).filter(isNew);
26903 if (deps.length === 0) {
26904 sorted[next['@id']] = next;
26905 processing.shift();
26907 processing = deps.concat(processing);
26912 changes.relation = Object.values(sorted);
26916 function rep(entity) {
26917 return entity.asJXON(changeset_id);
26923 '@generator': 'iD',
26924 'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
26925 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
26926 'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {
26932 asGeoJSON: function asGeoJSON() {
26937 function osmNote() {
26938 if (!(this instanceof osmNote)) {
26939 return new osmNote().initialize(arguments);
26940 } else if (arguments.length) {
26941 this.initialize(arguments);
26945 osmNote.id = function () {
26946 return osmNote.id.next--;
26949 osmNote.id.next = -1;
26950 Object.assign(osmNote.prototype, {
26952 initialize: function initialize(sources) {
26953 for (var i = 0; i < sources.length; ++i) {
26954 var source = sources[i];
26956 for (var prop in source) {
26957 if (Object.prototype.hasOwnProperty.call(source, prop)) {
26958 if (source[prop] === undefined) {
26961 this[prop] = source[prop];
26968 this.id = osmNote.id().toString();
26973 extent: function extent() {
26974 return new geoExtent(this.loc);
26976 update: function update(attrs) {
26977 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
26979 isNew: function isNew() {
26980 return this.id < 0;
26982 move: function move(loc) {
26983 return this.update({
26989 function osmRelation() {
26990 if (!(this instanceof osmRelation)) {
26991 return new osmRelation().initialize(arguments);
26992 } else if (arguments.length) {
26993 this.initialize(arguments);
26996 osmEntity.relation = osmRelation;
26997 osmRelation.prototype = Object.create(osmEntity.prototype);
26999 osmRelation.creationOrder = function (a, b) {
27000 var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
27001 var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
27002 if (aId < 0 || bId < 0) return aId - bId;
27006 Object.assign(osmRelation.prototype, {
27009 copy: function copy(resolver, copies) {
27010 if (copies[this.id]) return copies[this.id];
27011 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
27012 var members = this.members.map(function (member) {
27013 return Object.assign({}, member, {
27014 id: resolver.entity(member.id).copy(resolver, copies).id
27017 copy = copy.update({
27020 copies[this.id] = copy;
27023 extent: function extent(resolver, memo) {
27024 return resolver["transient"](this, 'extent', function () {
27025 if (memo && memo[this.id]) return geoExtent();
27027 memo[this.id] = true;
27028 var extent = geoExtent();
27030 for (var i = 0; i < this.members.length; i++) {
27031 var member = resolver.hasEntity(this.members[i].id);
27034 extent._extend(member.extent(resolver, memo));
27041 geometry: function geometry(graph) {
27042 return graph["transient"](this, 'geometry', function () {
27043 return this.isMultipolygon() ? 'area' : 'relation';
27046 isDegenerate: function isDegenerate() {
27047 return this.members.length === 0;
27049 // Return an array of members, each extended with an 'index' property whose value
27050 // is the member index.
27051 indexedMembers: function indexedMembers() {
27052 var result = new Array(this.members.length);
27054 for (var i = 0; i < this.members.length; i++) {
27055 result[i] = Object.assign({}, this.members[i], {
27062 // Return the first member with the given role. A copy of the member object
27063 // is returned, extended with an 'index' property whose value is the member index.
27064 memberByRole: function memberByRole(role) {
27065 for (var i = 0; i < this.members.length; i++) {
27066 if (this.members[i].role === role) {
27067 return Object.assign({}, this.members[i], {
27073 // Same as memberByRole, but returns all members with the given role
27074 membersByRole: function membersByRole(role) {
27077 for (var i = 0; i < this.members.length; i++) {
27078 if (this.members[i].role === role) {
27079 result.push(Object.assign({}, this.members[i], {
27087 // Return the first member with the given id. A copy of the member object
27088 // is returned, extended with an 'index' property whose value is the member index.
27089 memberById: function memberById(id) {
27090 for (var i = 0; i < this.members.length; i++) {
27091 if (this.members[i].id === id) {
27092 return Object.assign({}, this.members[i], {
27098 // Return the first member with the given id and role. A copy of the member object
27099 // is returned, extended with an 'index' property whose value is the member index.
27100 memberByIdAndRole: function memberByIdAndRole(id, role) {
27101 for (var i = 0; i < this.members.length; i++) {
27102 if (this.members[i].id === id && this.members[i].role === role) {
27103 return Object.assign({}, this.members[i], {
27109 addMember: function addMember(member, index) {
27110 var members = this.members.slice();
27111 members.splice(index === undefined ? members.length : index, 0, member);
27112 return this.update({
27116 updateMember: function updateMember(member, index) {
27117 var members = this.members.slice();
27118 members.splice(index, 1, Object.assign({}, members[index], member));
27119 return this.update({
27123 removeMember: function removeMember(index) {
27124 var members = this.members.slice();
27125 members.splice(index, 1);
27126 return this.update({
27130 removeMembersWithID: function removeMembersWithID(id) {
27131 var members = this.members.filter(function (m) {
27132 return m.id !== id;
27134 return this.update({
27138 moveMember: function moveMember(fromIndex, toIndex) {
27139 var members = this.members.slice();
27140 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
27141 return this.update({
27145 // Wherever a member appears with id `needle.id`, replace it with a member
27146 // with id `replacement.id`, type `replacement.type`, and the original role,
27147 // By default, adding a duplicate member (by id and role) is prevented.
27148 // Return an updated relation.
27149 replaceMember: function replaceMember(needle, replacement, keepDuplicates) {
27150 if (!this.memberById(needle.id)) return this;
27153 for (var i = 0; i < this.members.length; i++) {
27154 var member = this.members[i];
27156 if (member.id !== needle.id) {
27157 members.push(member);
27158 } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
27160 id: replacement.id,
27161 type: replacement.type,
27167 return this.update({
27171 asJXON: function asJXON(changeset_id) {
27174 '@id': this.osmId(),
27175 '@version': this.version || 0,
27176 member: this.members.map(function (member) {
27181 ref: osmEntity.id.toOSM(member.id)
27185 tag: Object.keys(this.tags).map(function (k) {
27196 if (changeset_id) {
27197 r.relation['@changeset'] = changeset_id;
27202 asGeoJSON: function asGeoJSON(resolver) {
27203 return resolver["transient"](this, 'GeoJSON', function () {
27204 if (this.isMultipolygon()) {
27206 type: 'MultiPolygon',
27207 coordinates: this.multipolygon(resolver)
27211 type: 'FeatureCollection',
27212 properties: this.tags,
27213 features: this.members.map(function (member) {
27214 return Object.assign({
27216 }, resolver.entity(member.id).asGeoJSON(resolver));
27222 area: function area(resolver) {
27223 return resolver["transient"](this, 'area', function () {
27224 return d3_geoArea(this.asGeoJSON(resolver));
27227 isMultipolygon: function isMultipolygon() {
27228 return this.tags.type === 'multipolygon';
27230 isComplete: function isComplete(resolver) {
27231 for (var i = 0; i < this.members.length; i++) {
27232 if (!resolver.hasEntity(this.members[i].id)) {
27239 hasFromViaTo: function hasFromViaTo() {
27240 return this.members.some(function (m) {
27241 return m.role === 'from';
27242 }) && this.members.some(function (m) {
27243 return m.role === 'via';
27244 }) && this.members.some(function (m) {
27245 return m.role === 'to';
27248 isRestriction: function isRestriction() {
27249 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
27251 isValidRestriction: function isValidRestriction() {
27252 if (!this.isRestriction()) return false;
27253 var froms = this.members.filter(function (m) {
27254 return m.role === 'from';
27256 var vias = this.members.filter(function (m) {
27257 return m.role === 'via';
27259 var tos = this.members.filter(function (m) {
27260 return m.role === 'to';
27262 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') return false;
27263 if (froms.some(function (m) {
27264 return m.type !== 'way';
27266 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') return false;
27267 if (tos.some(function (m) {
27268 return m.type !== 'way';
27270 if (vias.length === 0) return false;
27271 if (vias.length > 1 && vias.some(function (m) {
27272 return m.type !== 'way';
27276 // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
27277 // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
27279 // This corresponds to the structure needed for rendering a multipolygon path using a
27280 // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
27282 // In the case of invalid geometries, this function will still return a result which
27283 // includes the nodes of all way members, but some Nds may be unclosed and some inner
27284 // rings not matched with the intended outer ring.
27286 multipolygon: function multipolygon(resolver) {
27287 var outers = this.members.filter(function (m) {
27288 return 'outer' === (m.role || 'outer');
27290 var inners = this.members.filter(function (m) {
27291 return 'inner' === m.role;
27293 outers = osmJoinWays(outers, resolver);
27294 inners = osmJoinWays(inners, resolver);
27296 var sequenceToLineString = function sequenceToLineString(sequence) {
27297 if (sequence.nodes.length > 2 && sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
27298 // close unclosed parts to ensure correct area rendering - #2945
27299 sequence.nodes.push(sequence.nodes[0]);
27302 return sequence.nodes.map(function (node) {
27307 outers = outers.map(sequenceToLineString);
27308 inners = inners.map(sequenceToLineString);
27309 var result = outers.map(function (o) {
27310 // Heuristic for detecting counterclockwise winding order. Assumes
27311 // that OpenStreetMap polygons are not hemisphere-spanning.
27312 return [d3_geoArea({
27315 }) > 2 * Math.PI ? o.reverse() : o];
27318 function findOuter(inner) {
27321 for (o = 0; o < outers.length; o++) {
27323 if (geoPolygonContainsPolygon(outer, inner)) return o;
27326 for (o = 0; o < outers.length; o++) {
27328 if (geoPolygonIntersectsPolygon(outer, inner, false)) return o;
27332 for (var i = 0; i < inners.length; i++) {
27333 var inner = inners[i];
27337 coordinates: [inner]
27338 }) < 2 * Math.PI) {
27339 inner = inner.reverse();
27342 var o = findOuter(inners[i]);
27344 if (o !== undefined) {
27345 result[o].push(inners[i]);
27347 result.push([inners[i]]); // Invalid geometry
27355 var QAItem = /*#__PURE__*/function () {
27356 function QAItem(loc, service, itemType, id, props) {
27357 _classCallCheck(this, QAItem);
27359 // Store required properties
27361 this.service = service.title;
27362 this.itemType = itemType; // All issues must have an ID for selection, use generic if none specified
27364 this.id = id ? id : "".concat(QAItem.id());
27365 this.update(props); // Some QA services have marker icons to differentiate issues
27367 if (service && typeof service.getIcon === 'function') {
27368 this.icon = service.getIcon(itemType);
27372 _createClass(QAItem, [{
27374 value: function update(props) {
27377 // You can't override this initial information
27378 var loc = this.loc,
27379 service = this.service,
27380 itemType = this.itemType,
27382 Object.keys(props).forEach(function (prop) {
27383 return _this[prop] = props[prop];
27386 this.service = service;
27387 this.itemType = itemType;
27390 } // Generic handling for newly created QAItems
27394 value: function id() {
27395 return this.nextId--;
27401 QAItem.nextId = -1;
27404 // Optionally, split only the given ways, if multiple ways share
27407 // This is the inverse of `iD.actionJoin`.
27409 // For testing convenience, accepts an ID to assign to the new way.
27410 // Normally, this will be undefined and the way will automatically
27411 // be assigned a new ID.
27414 // https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
27417 function actionSplit(nodeIds, newWayIds) {
27418 // accept single ID for backwards-compatiblity
27419 if (typeof nodeIds === 'string') nodeIds = [nodeIds];
27421 var _wayIDs; // the strategy for picking which way will have a new version and which way is newly created
27424 var _keepHistoryOn = 'longest'; // 'longest', 'first'
27425 // The IDs of the ways actually created by running this action
27427 var _createdWayIDs = [];
27429 function dist(graph, nA, nB) {
27430 var locA = graph.entity(nA).loc;
27431 var locB = graph.entity(nB).loc;
27432 var epsilon = 1e-6;
27433 return locA && locB ? geoSphericalDistance(locA, locB) : epsilon;
27434 } // If the way is closed, we need to search for a partner node
27435 // to split the way at.
27437 // The following looks for a node that is both far away from
27438 // the initial node in terms of way segment length and nearby
27439 // in terms of beeline-distance. This assures that areas get
27440 // split on the most "natural" points (independent of the number
27442 // For example: bone-shaped areas get split across their waist
27443 // line, circles across the diameter.
27446 function splitArea(nodes, idxA, graph) {
27447 var lengths = new Array(nodes.length);
27453 function wrap(index) {
27454 return utilWrap(index, nodes.length);
27455 } // calculate lengths
27460 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
27461 length += dist(graph, nodes[i], nodes[wrap(i - 1)]);
27462 lengths[i] = length;
27467 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
27468 length += dist(graph, nodes[i], nodes[wrap(i + 1)]);
27470 if (length < lengths[i]) {
27471 lengths[i] = length;
27473 } // determine best opposite node to split
27476 for (i = 0; i < nodes.length; i++) {
27477 var cost = lengths[i] / dist(graph, nodes[idxA], nodes[i]);
27488 function totalLengthBetweenNodes(graph, nodes) {
27489 var totalLength = 0;
27491 for (var i = 0; i < nodes.length - 1; i++) {
27492 totalLength += dist(graph, nodes[i], nodes[i + 1]);
27495 return totalLength;
27498 function split(graph, nodeId, wayA, newWayId) {
27499 var wayB = osmWay({
27502 }); // `wayB` is the NEW way
27504 var origNodes = wayA.nodes.slice();
27507 var isArea = wayA.isArea();
27508 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
27510 if (wayA.isClosed()) {
27511 var nodes = wayA.nodes.slice(0, -1);
27512 var idxA = nodes.indexOf(nodeId);
27513 var idxB = splitArea(nodes, idxA, graph);
27516 nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
27517 nodesB = nodes.slice(idxB, idxA + 1);
27519 nodesA = nodes.slice(idxA, idxB + 1);
27520 nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
27523 var idx = wayA.nodes.indexOf(nodeId, 1);
27524 nodesA = wayA.nodes.slice(0, idx + 1);
27525 nodesB = wayA.nodes.slice(idx);
27528 var lengthA = totalLengthBetweenNodes(graph, nodesA);
27529 var lengthB = totalLengthBetweenNodes(graph, nodesB);
27531 if (_keepHistoryOn === 'longest' && lengthB > lengthA) {
27532 // keep the history on the longer way, regardless of the node count
27533 wayA = wayA.update({
27536 wayB = wayB.update({
27539 var temp = lengthA;
27543 wayA = wayA.update({
27546 wayB = wayB.update({
27551 if (wayA.tags.step_count) {
27552 // divide up the the step count proportionally between the two ways
27553 var stepCount = parseFloat(wayA.tags.step_count);
27555 if (stepCount && // ensure a number
27556 isFinite(stepCount) && // ensure positive
27557 stepCount > 0 && // ensure integer
27558 Math.round(stepCount) === stepCount) {
27559 var tagsA = Object.assign({}, wayA.tags);
27560 var tagsB = Object.assign({}, wayB.tags);
27561 var ratioA = lengthA / (lengthA + lengthB);
27562 var countA = Math.round(stepCount * ratioA);
27563 tagsA.step_count = countA.toString();
27564 tagsB.step_count = (stepCount - countA).toString();
27565 wayA = wayA.update({
27568 wayB = wayB.update({
27574 graph = graph.replace(wayA);
27575 graph = graph.replace(wayB);
27576 graph.parentRelations(wayA).forEach(function (relation) {
27577 var member; // Turn restrictions - make sure:
27578 // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
27579 // (whichever one is connected to the VIA node/ways)
27580 // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
27582 if (relation.hasFromViaTo()) {
27583 var f = relation.memberByRole('from');
27584 var v = relation.membersByRole('via');
27585 var t = relation.memberByRole('to');
27586 var i; // 1. split a FROM/TO
27588 if (f.id === wayA.id || t.id === wayA.id) {
27591 if (v.length === 1 && v[0].type === 'node') {
27593 keepB = wayB.contains(v[0].id);
27595 // check via way(s)
27596 for (i = 0; i < v.length; i++) {
27597 if (v[i].type === 'way') {
27598 var wayVia = graph.hasEntity(v[i].id);
27600 if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
27609 relation = relation.replaceMember(wayA, wayB);
27610 graph = graph.replace(relation);
27611 } // 2. split a VIA
27614 for (i = 0; i < v.length; i++) {
27615 if (v[i].type === 'way' && v[i].id === wayA.id) {
27621 graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
27625 } // All other relations (Routes, Multipolygons, etc):
27626 // 1. Both `wayA` and `wayB` remain in the relation
27627 // 2. But must be inserted as a pair (see `actionAddMember` for details)
27630 if (relation === isOuter) {
27631 graph = graph.replace(relation.mergeTags(wayA.tags));
27632 graph = graph.replace(wayA.update({
27635 graph = graph.replace(wayB.update({
27643 role: relation.memberById(wayA.id).role
27646 originalID: wayA.id,
27647 insertedID: wayB.id,
27650 graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
27654 if (!isOuter && isArea) {
27655 var multipolygon = osmRelation({
27656 tags: Object.assign({}, wayA.tags, {
27657 type: 'multipolygon'
27669 graph = graph.replace(multipolygon);
27670 graph = graph.replace(wayA.update({
27673 graph = graph.replace(wayB.update({
27678 _createdWayIDs.push(wayB.id);
27683 var action = function action(graph) {
27684 _createdWayIDs = [];
27685 var newWayIndex = 0;
27687 for (var i = 0; i < nodeIds.length; i++) {
27688 var nodeId = nodeIds[i];
27689 var candidates = action.waysForNode(nodeId, graph);
27691 for (var j = 0; j < candidates.length; j++) {
27692 graph = split(graph, nodeId, candidates[j], newWayIds && newWayIds[newWayIndex]);
27700 action.getCreatedWayIDs = function () {
27701 return _createdWayIDs;
27704 action.waysForNode = function (nodeId, graph) {
27705 var node = graph.entity(nodeId);
27706 var splittableParents = graph.parentWays(node).filter(isSplittable);
27709 // If the ways to split aren't specified, only split the lines.
27710 // If there are no lines to split, split the areas.
27711 var hasLine = splittableParents.some(function (parent) {
27712 return parent.geometry(graph) === 'line';
27716 return splittableParents.filter(function (parent) {
27717 return parent.geometry(graph) === 'line';
27722 return splittableParents;
27724 function isSplittable(parent) {
27725 // If the ways to split are specified, ignore everything else.
27726 if (_wayIDs && _wayIDs.indexOf(parent.id) === -1) return false; // We can fake splitting closed ways at their endpoints...
27728 if (parent.isClosed()) return true; // otherwise, we can't split nodes at their endpoints.
27730 for (var i = 1; i < parent.nodes.length - 1; i++) {
27731 if (parent.nodes[i] === nodeId) return true;
27738 action.ways = function (graph) {
27739 return utilArrayUniq([].concat.apply([], nodeIds.map(function (nodeId) {
27740 return action.waysForNode(nodeId, graph);
27744 action.disabled = function (graph) {
27745 for (var i = 0; i < nodeIds.length; i++) {
27746 var nodeId = nodeIds[i];
27747 var candidates = action.waysForNode(nodeId, graph);
27749 if (candidates.length === 0 || _wayIDs && _wayIDs.length !== candidates.length) {
27750 return 'not_eligible';
27755 action.limitWays = function (val) {
27756 if (!arguments.length) return _wayIDs;
27761 action.keepHistoryOn = function (val) {
27762 if (!arguments.length) return _keepHistoryOn;
27763 _keepHistoryOn = val;
27770 function coreGraph(other, mutable) {
27771 if (!(this instanceof coreGraph)) return new coreGraph(other, mutable);
27773 if (other instanceof coreGraph) {
27774 var base = other.base();
27775 this.entities = Object.assign(Object.create(base.entities), other.entities);
27776 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
27777 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
27779 this.entities = Object.create({});
27780 this._parentWays = Object.create({});
27781 this._parentRels = Object.create({});
27782 this.rebase(other || [], [this]);
27785 this.transients = {};
27786 this._childNodes = {};
27787 this.frozen = !mutable;
27789 coreGraph.prototype = {
27790 hasEntity: function hasEntity(id) {
27791 return this.entities[id];
27793 entity: function entity(id) {
27794 var entity = this.entities[id]; //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
27797 entity = this.entities.__proto__[id]; // eslint-disable-line no-proto
27801 throw new Error('entity ' + id + ' not found');
27806 geometry: function geometry(id) {
27807 return this.entity(id).geometry(this);
27809 "transient": function transient(entity, key, fn) {
27810 var id = entity.id;
27811 var transients = this.transients[id] || (this.transients[id] = {});
27813 if (transients[key] !== undefined) {
27814 return transients[key];
27817 transients[key] = fn.call(entity);
27818 return transients[key];
27820 parentWays: function parentWays(entity) {
27821 var parents = this._parentWays[entity.id];
27825 parents.forEach(function (id) {
27826 result.push(this.entity(id));
27832 isPoi: function isPoi(entity) {
27833 var parents = this._parentWays[entity.id];
27834 return !parents || parents.size === 0;
27836 isShared: function isShared(entity) {
27837 var parents = this._parentWays[entity.id];
27838 return parents && parents.size > 1;
27840 parentRelations: function parentRelations(entity) {
27841 var parents = this._parentRels[entity.id];
27845 parents.forEach(function (id) {
27846 result.push(this.entity(id));
27852 parentMultipolygons: function parentMultipolygons(entity) {
27853 return this.parentRelations(entity).filter(function (relation) {
27854 return relation.isMultipolygon();
27857 childNodes: function childNodes(entity) {
27858 if (this._childNodes[entity.id]) return this._childNodes[entity.id];
27859 if (!entity.nodes) return [];
27862 for (var i = 0; i < entity.nodes.length; i++) {
27863 nodes[i] = this.entity(entity.nodes[i]);
27865 this._childNodes[entity.id] = nodes;
27866 return this._childNodes[entity.id];
27868 base: function base() {
27870 'entities': Object.getPrototypeOf(this.entities),
27871 'parentWays': Object.getPrototypeOf(this._parentWays),
27872 'parentRels': Object.getPrototypeOf(this._parentRels)
27875 // Unlike other graph methods, rebase mutates in place. This is because it
27876 // is used only during the history operation that merges newly downloaded
27877 // data into each state. To external consumers, it should appear as if the
27878 // graph always contained the newly downloaded data.
27879 rebase: function rebase(entities, stack, force) {
27880 var base = this.base();
27883 for (i = 0; i < entities.length; i++) {
27884 var entity = entities[i];
27885 if (!entity.visible || !force && base.entities[entity.id]) continue; // Merging data into the base graph
27887 base.entities[entity.id] = entity;
27889 this._updateCalculated(undefined, entity, base.parentWays, base.parentRels); // Restore provisionally-deleted nodes that are discovered to have an extant parent
27892 if (entity.type === 'way') {
27893 for (j = 0; j < entity.nodes.length; j++) {
27894 id = entity.nodes[j];
27896 for (k = 1; k < stack.length; k++) {
27897 var ents = stack[k].entities;
27899 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
27907 for (i = 0; i < stack.length; i++) {
27908 stack[i]._updateRebased();
27911 _updateRebased: function _updateRebased() {
27912 var base = this.base();
27913 Object.keys(this._parentWays).forEach(function (child) {
27914 if (base.parentWays[child]) {
27915 base.parentWays[child].forEach(function (id) {
27916 if (!this.entities.hasOwnProperty(id)) {
27917 this._parentWays[child].add(id);
27922 Object.keys(this._parentRels).forEach(function (child) {
27923 if (base.parentRels[child]) {
27924 base.parentRels[child].forEach(function (id) {
27925 if (!this.entities.hasOwnProperty(id)) {
27926 this._parentRels[child].add(id);
27931 this.transients = {}; // this._childNodes is not updated, under the assumption that
27932 // ways are always downloaded with their child nodes.
27934 // Updates calculated properties (parentWays, parentRels) for the specified change
27935 _updateCalculated: function _updateCalculated(oldentity, entity, parentWays, parentRels) {
27936 parentWays = parentWays || this._parentWays;
27937 parentRels = parentRels || this._parentRels;
27938 var type = entity && entity.type || oldentity && oldentity.type;
27939 var removed, added, i;
27941 if (type === 'way') {
27942 // Update parentWays
27943 if (oldentity && entity) {
27944 removed = utilArrayDifference(oldentity.nodes, entity.nodes);
27945 added = utilArrayDifference(entity.nodes, oldentity.nodes);
27946 } else if (oldentity) {
27947 removed = oldentity.nodes;
27949 } else if (entity) {
27951 added = entity.nodes;
27954 for (i = 0; i < removed.length; i++) {
27955 // make a copy of prototype property, store as own property, and update..
27956 parentWays[removed[i]] = new Set(parentWays[removed[i]]);
27957 parentWays[removed[i]]["delete"](oldentity.id);
27960 for (i = 0; i < added.length; i++) {
27961 // make a copy of prototype property, store as own property, and update..
27962 parentWays[added[i]] = new Set(parentWays[added[i]]);
27963 parentWays[added[i]].add(entity.id);
27965 } else if (type === 'relation') {
27966 // Update parentRels
27967 // diff only on the IDs since the same entity can be a member multiple times with different roles
27968 var oldentityMemberIDs = oldentity ? oldentity.members.map(function (m) {
27971 var entityMemberIDs = entity ? entity.members.map(function (m) {
27975 if (oldentity && entity) {
27976 removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
27977 added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
27978 } else if (oldentity) {
27979 removed = oldentityMemberIDs;
27981 } else if (entity) {
27983 added = entityMemberIDs;
27986 for (i = 0; i < removed.length; i++) {
27987 // make a copy of prototype property, store as own property, and update..
27988 parentRels[removed[i]] = new Set(parentRels[removed[i]]);
27989 parentRels[removed[i]]["delete"](oldentity.id);
27992 for (i = 0; i < added.length; i++) {
27993 // make a copy of prototype property, store as own property, and update..
27994 parentRels[added[i]] = new Set(parentRels[added[i]]);
27995 parentRels[added[i]].add(entity.id);
27999 replace: function replace(entity) {
28000 if (this.entities[entity.id] === entity) return this;
28001 return this.update(function () {
28002 this._updateCalculated(this.entities[entity.id], entity);
28004 this.entities[entity.id] = entity;
28007 remove: function remove(entity) {
28008 return this.update(function () {
28009 this._updateCalculated(entity, undefined);
28011 this.entities[entity.id] = undefined;
28014 revert: function revert(id) {
28015 var baseEntity = this.base().entities[id];
28016 var headEntity = this.entities[id];
28017 if (headEntity === baseEntity) return this;
28018 return this.update(function () {
28019 this._updateCalculated(headEntity, baseEntity);
28021 delete this.entities[id];
28024 update: function update() {
28025 var graph = this.frozen ? coreGraph(this, true) : this;
28027 for (var i = 0; i < arguments.length; i++) {
28028 arguments[i].call(graph, graph);
28031 if (this.frozen) graph.frozen = true;
28034 // Obliterates any existing entities
28035 load: function load(entities) {
28036 var base = this.base();
28037 this.entities = Object.create(base.entities);
28039 for (var i in entities) {
28040 this.entities[i] = entities[i];
28042 this._updateCalculated(base.entities[i], this.entities[i]);
28049 function osmTurn(turn) {
28050 if (!(this instanceof osmTurn)) {
28051 return new osmTurn(turn);
28054 Object.assign(this, turn);
28056 function osmIntersection(graph, startVertexId, maxDistance) {
28057 maxDistance = maxDistance || 30; // in meters
28059 var vgraph = coreGraph(); // virtual graph
28063 function memberOfRestriction(entity) {
28064 return graph.parentRelations(entity).some(function (r) {
28065 return r.isRestriction();
28069 function isRoad(way) {
28070 if (way.isArea() || way.isDegenerate()) return false;
28073 'motorway_link': true,
28075 'trunk_link': true,
28077 'primary_link': true,
28079 'secondary_link': true,
28081 'tertiary_link': true,
28082 'residential': true,
28083 'unclassified': true,
28084 'living_street': true,
28089 return roads[way.tags.highway];
28092 var startNode = graph.entity(startVertexId);
28093 var checkVertices = [startNode];
28096 var vertexIds = [];
28104 var parent; // `actions` will store whatever actions must be performed to satisfy
28105 // preconditions for adding a turn restriction to this intersection.
28106 // - Remove any existing degenerate turn restrictions (missing from/to, etc)
28107 // - Reverse oneways so that they are drawn in the forward direction
28108 // - Split ways on key vertices
28110 var actions = []; // STEP 1: walk the graph outwards from starting vertex to search
28111 // for more key vertices and ways to include in the intersection..
28113 while (checkVertices.length) {
28114 vertex = checkVertices.pop(); // check this vertex for parent ways that are roads
28116 checkWays = graph.parentWays(vertex);
28117 var hasWays = false;
28119 for (i = 0; i < checkWays.length; i++) {
28120 way = checkWays[i];
28121 if (!isRoad(way) && !memberOfRestriction(way)) continue;
28122 ways.push(way); // it's a road, or it's already in a turn restriction
28124 hasWays = true; // check the way's children for more key vertices
28126 nodes = utilArrayUniq(graph.childNodes(way));
28128 for (j = 0; j < nodes.length; j++) {
28130 if (node === vertex) continue; // same thing
28132 if (vertices.indexOf(node) !== -1) continue; // seen it already
28134 if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) continue; // too far from start
28135 // a key vertex will have parents that are also roads
28137 var hasParents = false;
28138 parents = graph.parentWays(node);
28140 for (k = 0; k < parents.length; k++) {
28141 parent = parents[k];
28142 if (parent === way) continue; // same thing
28144 if (ways.indexOf(parent) !== -1) continue; // seen it already
28146 if (!isRoad(parent)) continue; // not a road
28153 checkVertices.push(node);
28159 vertices.push(vertex);
28163 vertices = utilArrayUniq(vertices);
28164 ways = utilArrayUniq(ways); // STEP 2: Build a virtual graph containing only the entities in the intersection..
28165 // Everything done after this step should act on the virtual graph
28166 // Any actions that must be performed later to the main graph go in `actions` array
28168 ways.forEach(function (way) {
28169 graph.childNodes(way).forEach(function (node) {
28170 vgraph = vgraph.replace(node);
28172 vgraph = vgraph.replace(way);
28173 graph.parentRelations(way).forEach(function (relation) {
28174 if (relation.isRestriction()) {
28175 if (relation.isValidRestriction(graph)) {
28176 vgraph = vgraph.replace(relation);
28177 } else if (relation.isComplete(graph)) {
28178 actions.push(actionDeleteRelation(relation.id));
28182 }); // STEP 3: Force all oneways to be drawn in the forward direction
28184 ways.forEach(function (w) {
28185 var way = vgraph.entity(w.id);
28187 if (way.tags.oneway === '-1') {
28188 var action = actionReverse(way.id, {
28189 reverseOneway: true
28191 actions.push(action);
28192 vgraph = action(vgraph);
28194 }); // STEP 4: Split ways on key vertices
28196 var origCount = osmEntity.id.next.way;
28197 vertices.forEach(function (v) {
28198 // This is an odd way to do it, but we need to find all the ways that
28199 // will be split here, then split them one at a time to ensure that these
28200 // actions can be replayed on the main graph exactly in the same order.
28201 // (It is unintuitive, but the order of ways returned from graph.parentWays()
28202 // is arbitrary, depending on how the main graph and vgraph were built)
28203 var splitAll = actionSplit([v.id]).keepHistoryOn('first');
28205 if (!splitAll.disabled(vgraph)) {
28206 splitAll.ways(vgraph).forEach(function (way) {
28207 var splitOne = actionSplit([v.id]).limitWays([way.id]).keepHistoryOn('first');
28208 actions.push(splitOne);
28209 vgraph = splitOne(vgraph);
28212 }); // In here is where we should also split the intersection at nearby junction.
28213 // for https://github.com/mapbox/iD-internal/issues/31
28214 // nearbyVertices.forEach(function(v) {
28216 // Reasons why we reset the way id count here:
28217 // 1. Continuity with way ids created by the splits so that we can replay
28218 // these actions later if the user decides to create a turn restriction
28219 // 2. Avoids churning way ids just by hovering over a vertex
28220 // and displaying the turn restriction editor
28222 osmEntity.id.next.way = origCount; // STEP 5: Update arrays to point to vgraph entities
28224 vertexIds = vertices.map(function (v) {
28229 vertexIds.forEach(function (id) {
28230 var vertex = vgraph.entity(id);
28231 var parents = vgraph.parentWays(vertex);
28232 vertices.push(vertex);
28233 ways = ways.concat(parents);
28235 vertices = utilArrayUniq(vertices);
28236 ways = utilArrayUniq(ways);
28237 vertexIds = vertices.map(function (v) {
28240 wayIds = ways.map(function (w) {
28242 }); // STEP 6: Update the ways with some metadata that will be useful for
28243 // walking the intersection graph later and rendering turn arrows.
28245 function withMetadata(way, vertexIds) {
28246 var __oneWay = way.isOneWay(); // which affixes are key vertices?
28249 var __first = vertexIds.indexOf(way.first()) !== -1;
28251 var __last = vertexIds.indexOf(way.last()) !== -1; // what roles is this way eligible for?
28254 var __via = __first && __last;
28256 var __from = __first && !__oneWay || __last;
28258 var __to = __first || __last && !__oneWay;
28260 return way.update({
28271 wayIds.forEach(function (id) {
28272 var way = withMetadata(vgraph.entity(id), vertexIds);
28273 vgraph = vgraph.replace(way);
28275 }); // STEP 7: Simplify - This is an iterative process where we:
28276 // 1. Find trivial vertices with only 2 parents
28277 // 2. trim off the leaf way from those vertices and remove from vgraph
28280 var removeWayIds = [];
28281 var removeVertexIds = [];
28285 checkVertices = vertexIds.slice();
28287 for (i = 0; i < checkVertices.length; i++) {
28288 var vertexId = checkVertices[i];
28289 vertex = vgraph.hasEntity(vertexId);
28292 if (vertexIds.indexOf(vertexId) !== -1) {
28293 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28296 removeVertexIds.push(vertexId);
28300 parents = vgraph.parentWays(vertex);
28302 if (parents.length < 3) {
28303 if (vertexIds.indexOf(vertexId) !== -1) {
28304 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28308 if (parents.length === 2) {
28309 // vertex with 2 parents is trivial
28310 var a = parents[0];
28311 var b = parents[1];
28312 var aIsLeaf = a && !a.__via;
28313 var bIsLeaf = b && !b.__via;
28314 var leaf, survivor;
28316 if (aIsLeaf && !bIsLeaf) {
28319 } else if (!aIsLeaf && bIsLeaf) {
28324 if (leaf && survivor) {
28325 survivor = withMetadata(survivor, vertexIds); // update survivor way
28327 vgraph = vgraph.replace(survivor).remove(leaf); // update graph
28329 removeWayIds.push(leaf.id);
28334 parents = vgraph.parentWays(vertex);
28336 if (parents.length < 2) {
28337 // vertex is no longer a key vertex
28338 if (vertexIds.indexOf(vertexId) !== -1) {
28339 vertexIds.splice(vertexIds.indexOf(vertexId), 1); // stop checking this one
28342 removeVertexIds.push(vertexId);
28346 if (parents.length < 1) {
28347 // vertex is no longer attached to anything
28348 vgraph = vgraph.remove(vertex);
28351 } while (keepGoing);
28353 vertices = vertices.filter(function (vertex) {
28354 return removeVertexIds.indexOf(vertex.id) === -1;
28355 }).map(function (vertex) {
28356 return vgraph.entity(vertex.id);
28358 ways = ways.filter(function (way) {
28359 return removeWayIds.indexOf(way.id) === -1;
28360 }).map(function (way) {
28361 return vgraph.entity(way.id);
28362 }); // OK! Here is our intersection..
28364 var intersection = {
28367 vertices: vertices,
28369 }; // Get all the valid turns through this intersection given a starting way id.
28370 // This operates on the virtual graph for everything.
28372 // Basically, walk through all possible paths from starting way,
28373 // honoring the existing turn restrictions as we go (watch out for loops!)
28375 // For each path found, generate and return a `osmTurn` datastructure.
28378 intersection.turns = function (fromWayId, maxViaWay) {
28379 if (!fromWayId) return [];
28380 if (!maxViaWay) maxViaWay = 0;
28381 var vgraph = intersection.graph;
28382 var keyVertexIds = intersection.vertices.map(function (v) {
28385 var start = vgraph.entity(fromWayId);
28386 if (!start || !(start.__from || start.__via)) return []; // maxViaWay=0 from-*-to (0 vias)
28387 // maxViaWay=1 from-*-via-*-to (1 via max)
28388 // maxViaWay=2 from-*-via-*-via-*-to (2 vias max)
28390 var maxPathLength = maxViaWay * 2 + 3;
28393 return turns; // traverse the intersection graph and find all the valid paths
28395 function step(entity, currPath, currRestrictions, matchedRestriction) {
28396 currPath = (currPath || []).slice(); // shallow copy
28398 if (currPath.length >= maxPathLength) return;
28399 currPath.push(entity.id);
28400 currRestrictions = (currRestrictions || []).slice(); // shallow copy
28404 if (entity.type === 'node') {
28405 var parents = vgraph.parentWays(entity);
28406 var nextWays = []; // which ways can we step into?
28408 for (i = 0; i < parents.length; i++) {
28409 var way = parents[i]; // if next way is a oneway incoming to this vertex, skip
28411 if (way.__oneWay && way.nodes[0] !== entity.id) continue; // if we have seen it before (allowing for an initial u-turn), skip
28413 if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) continue; // Check all "current" restrictions (where we've already walked the `FROM`)
28415 var restrict = null;
28417 for (j = 0; j < currRestrictions.length; j++) {
28418 var restriction = currRestrictions[j];
28419 var f = restriction.memberByRole('from');
28420 var v = restriction.membersByRole('via');
28421 var t = restriction.memberByRole('to');
28422 var isOnly = /^only_/.test(restriction.tags.restriction); // Does the current path match this turn restriction?
28424 var matchesFrom = f.id === fromWayId;
28425 var matchesViaTo = false;
28426 var isAlongOnlyPath = false;
28428 if (t.id === way.id) {
28430 if (v.length === 1 && v[0].type === 'node') {
28432 matchesViaTo = v[0].id === entity.id && (matchesFrom && currPath.length === 2 || !matchesFrom && currPath.length > 2);
28434 // match all VIA ways
28437 for (k = 2; k < currPath.length; k += 2) {
28438 // k = 2 skips FROM
28439 pathVias.push(currPath[k]); // (path goes way-node-way...)
28442 var restrictionVias = [];
28444 for (k = 0; k < v.length; k++) {
28445 if (v[k].type === 'way') {
28446 restrictionVias.push(v[k].id);
28450 var diff = utilArrayDifference(pathVias, restrictionVias);
28451 matchesViaTo = !diff.length;
28453 } else if (isOnly) {
28454 for (k = 0; k < v.length; k++) {
28455 // way doesn't match TO, but is one of the via ways along the path of an "only"
28456 if (v[k].type === 'way' && v[k].id === way.id) {
28457 isAlongOnlyPath = true;
28463 if (matchesViaTo) {
28466 id: restriction.id,
28467 direct: matchesFrom,
28474 id: restriction.id,
28475 direct: matchesFrom,
28482 // indirect - caused by a different nearby restriction
28483 if (isAlongOnlyPath) {
28485 id: restriction.id,
28491 } else if (isOnly) {
28493 id: restriction.id,
28500 } // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
28503 if (restrict && restrict.direct) break;
28512 nextWays.forEach(function (nextWay) {
28513 step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
28516 // entity.type === 'way'
28517 if (currPath.length >= 3) {
28518 // this is a "complete" path..
28519 var turnPath = currPath.slice(); // shallow copy
28520 // an indirect restriction - only include the partial path (starting at FROM)
28522 if (matchedRestriction && matchedRestriction.direct === false) {
28523 for (i = 0; i < turnPath.length; i++) {
28524 if (turnPath[i] === matchedRestriction.from) {
28525 turnPath = turnPath.slice(i);
28531 var turn = pathToTurn(turnPath);
28534 if (matchedRestriction) {
28535 turn.restrictionID = matchedRestriction.id;
28536 turn.no = matchedRestriction.no;
28537 turn.only = matchedRestriction.only;
28538 turn.direct = matchedRestriction.direct;
28541 turns.push(osmTurn(turn));
28544 if (currPath[0] === currPath[2]) return; // if we made a u-turn - stop here
28547 if (matchedRestriction && matchedRestriction.end) return; // don't advance any further
28548 // which nodes can we step into?
28550 var n1 = vgraph.entity(entity.first());
28551 var n2 = vgraph.entity(entity.last());
28552 var dist = geoSphericalDistance(n1.loc, n2.loc);
28553 var nextNodes = [];
28555 if (currPath.length > 1) {
28556 if (dist > maxDistance) return; // the next node is too far
28558 if (!entity.__via) return; // this way is a leaf / can't be a via
28561 if (!entity.__oneWay && // bidirectional..
28562 keyVertexIds.indexOf(n1.id) !== -1 && // key vertex..
28563 currPath.indexOf(n1.id) === -1) {
28564 // haven't seen it yet..
28565 nextNodes.push(n1); // can advance to first node
28568 if (keyVertexIds.indexOf(n2.id) !== -1 && // key vertex..
28569 currPath.indexOf(n2.id) === -1) {
28570 // haven't seen it yet..
28571 nextNodes.push(n2); // can advance to last node
28574 nextNodes.forEach(function (nextNode) {
28575 // gather restrictions FROM this way
28576 var fromRestrictions = vgraph.parentRelations(entity).filter(function (r) {
28577 if (!r.isRestriction()) return false;
28578 var f = r.memberByRole('from');
28579 if (!f || f.id !== entity.id) return false;
28580 var isOnly = /^only_/.test(r.tags.restriction);
28581 if (!isOnly) return true; // `only_` restrictions only matter along the direction of the VIA - #4849
28583 var isOnlyVia = false;
28584 var v = r.membersByRole('via');
28586 if (v.length === 1 && v[0].type === 'node') {
28588 isOnlyVia = v[0].id === nextNode.id;
28591 for (var i = 0; i < v.length; i++) {
28592 if (v[i].type !== 'way') continue;
28593 var viaWay = vgraph.entity(v[i].id);
28595 if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
28604 step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
28607 } // assumes path is alternating way-node-way of odd length
28610 function pathToTurn(path) {
28611 if (path.length < 3) return;
28612 var fromWayId, fromNodeId, fromVertexId;
28613 var toWayId, toNodeId, toVertexId;
28614 var viaWayIds, viaNodeId, isUturn;
28615 fromWayId = path[0];
28616 toWayId = path[path.length - 1];
28618 if (path.length === 3 && fromWayId === toWayId) {
28620 var way = vgraph.entity(fromWayId);
28621 if (way.__oneWay) return null;
28623 viaNodeId = fromVertexId = toVertexId = path[1];
28624 fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
28627 fromVertexId = path[1];
28628 fromNodeId = adjacentNode(fromWayId, fromVertexId);
28629 toVertexId = path[path.length - 2];
28630 toNodeId = adjacentNode(toWayId, toVertexId);
28632 if (path.length === 3) {
28633 viaNodeId = path[1];
28635 viaWayIds = path.filter(function (entityId) {
28636 return entityId[0] === 'w';
28638 viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1); // remove first, last
28643 key: path.join('_'),
28648 vertex: fromVertexId
28662 function adjacentNode(wayId, affixId) {
28663 var nodes = vgraph.entity(wayId).nodes;
28664 return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
28669 return intersection;
28671 function osmInferRestriction(graph, turn, projection) {
28672 var fromWay = graph.entity(turn.from.way);
28673 var fromNode = graph.entity(turn.from.node);
28674 var fromVertex = graph.entity(turn.from.vertex);
28675 var toWay = graph.entity(turn.to.way);
28676 var toNode = graph.entity(turn.to.node);
28677 var toVertex = graph.entity(turn.to.vertex);
28678 var fromOneWay = fromWay.tags.oneway === 'yes';
28679 var toOneWay = toWay.tags.oneway === 'yes';
28680 var angle = (geoAngle(fromVertex, fromNode, projection) - geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
28682 while (angle < 0) {
28686 if (fromNode === toNode) return 'no_u_turn';
28687 if ((angle < 23 || angle > 336) && fromOneWay && toOneWay) return 'no_u_turn'; // wider tolerance for u-turn if both ways are oneway
28689 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)
28691 if (angle < 158) return 'no_right_turn';
28692 if (angle > 202) return 'no_left_turn';
28693 return 'no_straight_on';
28696 function actionMergePolygon(ids, newRelationId) {
28697 function groupEntities(graph) {
28698 var entities = ids.map(function (id) {
28699 return graph.entity(id);
28701 var geometryGroups = utilArrayGroupBy(entities, function (entity) {
28702 if (entity.type === 'way' && entity.isClosed()) {
28703 return 'closedWay';
28704 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
28705 return 'multipolygon';
28710 return Object.assign({
28714 }, geometryGroups);
28717 var action = function action(graph) {
28718 var entities = groupEntities(graph); // An array representing all the polygons that are part of the multipolygon.
28720 // Each element is itself an array of objects with an id property, and has a
28721 // locs property which is an array of the locations forming the polygon.
28723 var polygons = entities.multipolygon.reduce(function (polygons, m) {
28724 return polygons.concat(osmJoinWays(m.members, graph));
28725 }, []).concat(entities.closedWay.map(function (d) {
28729 member.nodes = graph.childNodes(d);
28731 })); // contained is an array of arrays of boolean values,
28732 // where contained[j][k] is true iff the jth way is
28733 // contained by the kth way.
28735 var contained = polygons.map(function (w, i) {
28736 return polygons.map(function (d, n) {
28737 if (i === n) return null;
28738 return geoPolygonContainsPolygon(d.nodes.map(function (n) {
28740 }), w.nodes.map(function (n) {
28744 }); // Sort all polygons as either outer or inner ways
28749 while (polygons.length) {
28750 extractUncontained(polygons);
28751 polygons = polygons.filter(isContained);
28752 contained = contained.filter(isContained).map(filterContained);
28755 function isContained(d, i) {
28756 return contained[i].some(function (val) {
28761 function filterContained(d) {
28762 return d.filter(isContained);
28765 function extractUncontained(polygons) {
28766 polygons.forEach(function (d, i) {
28767 if (!isContained(d, i)) {
28768 d.forEach(function (member) {
28772 role: outer ? 'outer' : 'inner'
28778 } // Move all tags to one relation
28781 var relation = entities.multipolygon[0] || osmRelation({
28784 type: 'multipolygon'
28787 entities.multipolygon.slice(1).forEach(function (m) {
28788 relation = relation.mergeTags(m.tags);
28789 graph = graph.remove(m);
28791 entities.closedWay.forEach(function (way) {
28792 function isThisOuter(m) {
28793 return m.id === way.id && m.role !== 'inner';
28796 if (members.some(isThisOuter)) {
28797 relation = relation.mergeTags(way.tags);
28798 graph = graph.replace(way.update({
28803 return graph.replace(relation.update({
28805 tags: utilObjectOmit(relation.tags, ['area'])
28809 action.disabled = function (graph) {
28810 var entities = groupEntities(graph);
28812 if (entities.other.length > 0 || entities.closedWay.length + entities.multipolygon.length < 2) {
28813 return 'not_eligible';
28816 if (!entities.multipolygon.every(function (r) {
28817 return r.isComplete(graph);
28819 return 'incomplete_relation';
28822 if (!entities.multipolygon.length) {
28823 var sharedMultipolygons = [];
28824 entities.closedWay.forEach(function (way, i) {
28826 sharedMultipolygons = graph.parentMultipolygons(way);
28828 sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
28831 sharedMultipolygons = sharedMultipolygons.filter(function (relation) {
28832 return relation.members.length === entities.closedWay.length;
28835 if (sharedMultipolygons.length) {
28836 // don't create a new multipolygon if it'd be redundant
28837 return 'not_eligible';
28839 } else if (entities.closedWay.some(function (way) {
28840 return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
28842 // don't add a way to a multipolygon again if it's already a member
28843 return 'not_eligible';
28850 var UNSUPPORTED_Y$3 = regexpStickyHelpers.UNSUPPORTED_Y;
28852 // `RegExp.prototype.flags` getter
28853 // https://tc39.github.io/ecma262/#sec-get-regexp.prototype.flags
28854 if (descriptors && (/./g.flags != 'g' || UNSUPPORTED_Y$3)) {
28855 objectDefineProperty.f(RegExp.prototype, 'flags', {
28856 configurable: true,
28861 var fastDeepEqual = function equal(a, b) {
28862 if (a === b) return true;
28864 if (a && b && _typeof(a) == 'object' && _typeof(b) == 'object') {
28865 if (a.constructor !== b.constructor) return false;
28866 var length, i, keys;
28868 if (Array.isArray(a)) {
28870 if (length != b.length) return false;
28872 for (i = length; i-- !== 0;) {
28873 if (!equal(a[i], b[i])) return false;
28879 if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
28880 if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
28881 if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
28882 keys = Object.keys(a);
28883 length = keys.length;
28884 if (length !== Object.keys(b).length) return false;
28886 for (i = length; i-- !== 0;) {
28887 if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
28890 for (i = length; i-- !== 0;) {
28892 if (!equal(a[key], b[key])) return false;
28896 } // true if both NaN, false otherwise
28899 return a !== a && b !== b;
28902 // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
28903 // comparison, Bell Telephone Laboratories CSTR #41 (1976)
28904 // http://www.cs.dartmouth.edu/~doug/
28905 // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
28907 // Expects two arrays, finds longest common sequence
28909 function LCS(buffer1, buffer2) {
28910 var equivalenceClasses = {};
28912 for (var j = 0; j < buffer2.length; j++) {
28913 var item = buffer2[j];
28915 if (equivalenceClasses[item]) {
28916 equivalenceClasses[item].push(j);
28918 equivalenceClasses[item] = [j];
28927 var candidates = [NULLRESULT];
28929 for (var i = 0; i < buffer1.length; i++) {
28930 var _item = buffer1[i];
28931 var buffer2indices = equivalenceClasses[_item] || [];
28933 var c = candidates[0];
28935 for (var jx = 0; jx < buffer2indices.length; jx++) {
28936 var _j = buffer2indices[jx];
28939 for (s = r; s < candidates.length; s++) {
28940 if (candidates[s].buffer2index < _j && (s === candidates.length - 1 || candidates[s + 1].buffer2index > _j)) {
28945 if (s < candidates.length) {
28946 var newCandidate = {
28949 chain: candidates[s]
28952 if (r === candidates.length) {
28953 candidates.push(c);
28961 if (r === candidates.length) {
28962 break; // no point in examining further (j)s
28968 } // At this point, we know the LCS: it's in the reverse of the
28969 // linked-list through .chain of candidates[candidates.length - 1].
28972 return candidates[candidates.length - 1];
28973 } // We apply the LCS to build a 'comm'-style picture of the
28974 // offsets and lengths of mismatched chunks in the input
28975 // buffers. This is used by diff3MergeRegions.
28978 function diffIndices(buffer1, buffer2) {
28979 var lcs = LCS(buffer1, buffer2);
28981 var tail1 = buffer1.length;
28982 var tail2 = buffer2.length;
28984 for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
28985 var mismatchLength1 = tail1 - candidate.buffer1index - 1;
28986 var mismatchLength2 = tail2 - candidate.buffer2index - 1;
28987 tail1 = candidate.buffer1index;
28988 tail2 = candidate.buffer2index;
28990 if (mismatchLength1 || mismatchLength2) {
28992 buffer1: [tail1 + 1, mismatchLength1],
28993 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
28994 buffer2: [tail2 + 1, mismatchLength2],
28995 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
29002 } // We apply the LCS to build a JSON representation of a
29003 // independently derived from O, returns a fairly complicated
29004 // internal representation of merge decisions it's taken. The
29005 // interested reader may wish to consult
29007 // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
29008 // 'A Formal Investigation of ' In Arvind and Prasad,
29009 // editors, Foundations of Software Technology and Theoretical
29010 // Computer Science (FSTTCS), December 2007.
29012 // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
29016 function diff3MergeRegions(a, o, b) {
29017 // "hunks" are array subsets where `a` or `b` are different from `o`
29018 // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
29021 function addHunk(h, ab) {
29024 oStart: h.buffer1[0],
29025 oLength: h.buffer1[1],
29026 // length of o to remove
29027 abStart: h.buffer2[0],
29028 abLength: h.buffer2[1] // length of a/b to insert
29029 // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
29034 diffIndices(o, a).forEach(function (item) {
29035 return addHunk(item, 'a');
29037 diffIndices(o, b).forEach(function (item) {
29038 return addHunk(item, 'b');
29040 hunks.sort(function (x, y) {
29041 return x.oStart - y.oStart;
29044 var currOffset = 0;
29046 function advanceTo(endOffset) {
29047 if (endOffset > currOffset) {
29051 bufferStart: currOffset,
29052 bufferLength: endOffset - currOffset,
29053 bufferContent: o.slice(currOffset, endOffset)
29055 currOffset = endOffset;
29059 while (hunks.length) {
29060 var hunk = hunks.shift();
29061 var regionStart = hunk.oStart;
29062 var regionEnd = hunk.oStart + hunk.oLength;
29063 var regionHunks = [hunk];
29064 advanceTo(regionStart); // Try to pull next overlapping hunk into this region
29066 while (hunks.length) {
29067 var nextHunk = hunks[0];
29068 var nextHunkStart = nextHunk.oStart;
29069 if (nextHunkStart > regionEnd) break; // no overlap
29071 regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
29072 regionHunks.push(hunks.shift());
29075 if (regionHunks.length === 1) {
29076 // Only one hunk touches this region, meaning that there is no conflict here.
29077 // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
29078 if (hunk.abLength > 0) {
29079 var buffer = hunk.ab === 'a' ? a : b;
29083 bufferStart: hunk.abStart,
29084 bufferLength: hunk.abLength,
29085 bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
29089 // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
29090 // Effectively merge all the `a` hunks into one giant hunk, then do the
29091 // same for the `b` hunks; then, correct for skew in the regions of `o`
29092 // that each side changed, and report appropriate spans for the three sides.
29094 a: [a.length, -1, o.length, -1],
29095 b: [b.length, -1, o.length, -1]
29098 while (regionHunks.length) {
29099 hunk = regionHunks.shift();
29100 var oStart = hunk.oStart;
29101 var oEnd = oStart + hunk.oLength;
29102 var abStart = hunk.abStart;
29103 var abEnd = abStart + hunk.abLength;
29104 var _b = bounds[hunk.ab];
29105 _b[0] = Math.min(abStart, _b[0]);
29106 _b[1] = Math.max(abEnd, _b[1]);
29107 _b[2] = Math.min(oStart, _b[2]);
29108 _b[3] = Math.max(oEnd, _b[3]);
29111 var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
29112 var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
29113 var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
29114 var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
29118 aLength: aEnd - aStart,
29119 aContent: a.slice(aStart, aEnd),
29120 oStart: regionStart,
29121 oLength: regionEnd - regionStart,
29122 oContent: o.slice(regionStart, regionEnd),
29124 bLength: bEnd - bStart,
29125 bContent: b.slice(bStart, bEnd)
29127 results.push(result);
29130 currOffset = regionEnd;
29133 advanceTo(o.length);
29135 } // Applies the output of diff3MergeRegions to actually
29136 // construct the merged buffer; the returned result alternates
29137 // between 'ok' and 'conflict' blocks.
29138 // A "false conflict" is where `a` and `b` both change the same from `o`
29141 function diff3Merge(a, o, b, options) {
29143 excludeFalseConflicts: true,
29144 stringSeparator: /\s+/
29146 options = Object.assign(defaults, options);
29147 var aString = typeof a === 'string';
29148 var oString = typeof o === 'string';
29149 var bString = typeof b === 'string';
29150 if (aString) a = a.split(options.stringSeparator);
29151 if (oString) o = o.split(options.stringSeparator);
29152 if (bString) b = b.split(options.stringSeparator);
29154 var regions = diff3MergeRegions(a, o, b);
29157 function flushOk() {
29158 if (okBuffer.length) {
29167 function isFalseConflict(a, b) {
29168 if (a.length !== b.length) return false;
29170 for (var i = 0; i < a.length; i++) {
29171 if (a[i] !== b[i]) return false;
29177 regions.forEach(function (region) {
29178 if (region.stable) {
29181 (_okBuffer = okBuffer).push.apply(_okBuffer, _toConsumableArray(region.bufferContent));
29183 if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
29186 (_okBuffer2 = okBuffer).push.apply(_okBuffer2, _toConsumableArray(region.aContent));
29191 a: region.aContent,
29192 aIndex: region.aStart,
29193 o: region.oContent,
29194 oIndex: region.oStart,
29195 b: region.bContent,
29196 bIndex: region.bStart
29206 function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
29207 discardTags = discardTags || {};
29208 var _option = 'safe'; // 'safe', 'force_local', 'force_remote'
29210 var _conflicts = [];
29213 return typeof formatUser === 'function' ? formatUser(d) : d;
29216 function mergeLocation(remote, target) {
29217 function pointEqual(a, b) {
29218 var epsilon = 1e-6;
29219 return Math.abs(a[0] - b[0]) < epsilon && Math.abs(a[1] - b[1]) < epsilon;
29222 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
29226 if (_option === 'force_remote') {
29227 return target.update({
29232 _conflicts.push(_t('merge_remote_changes.conflict.location', {
29233 user: user(remote.user)
29239 function mergeNodes(base, remote, target) {
29240 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
29244 if (_option === 'force_remote') {
29245 return target.update({
29246 nodes: remote.nodes
29250 var ccount = _conflicts.length;
29251 var o = base.nodes || [];
29252 var a = target.nodes || [];
29253 var b = remote.nodes || [];
29255 var hunks = diff3Merge(a, o, b, {
29256 excludeFalseConflicts: true
29259 for (var i = 0; i < hunks.length; i++) {
29260 var hunk = hunks[i];
29263 nodes.push.apply(nodes, hunk.ok);
29265 // for all conflicts, we can assume c.a !== c.b
29266 // because `diff3Merge` called with `true` option to exclude false conflicts..
29267 var c = hunk.conflict;
29269 if (fastDeepEqual(c.o, c.a)) {
29270 // only changed remotely
29271 nodes.push.apply(nodes, c.b);
29272 } else if (fastDeepEqual(c.o, c.b)) {
29273 // only changed locally
29274 nodes.push.apply(nodes, c.a);
29276 // changed both locally and remotely
29277 _conflicts.push(_t('merge_remote_changes.conflict.nodelist', {
29278 user: user(remote.user)
29286 return _conflicts.length === ccount ? target.update({
29291 function mergeChildren(targetWay, children, updates, graph) {
29292 function isUsed(node, targetWay) {
29293 var hasInterestingParent = graph.parentWays(node).some(function (way) {
29294 return way.id !== targetWay.id;
29296 return node.hasInterestingTags() || hasInterestingParent || graph.parentRelations(node).length > 0;
29299 var ccount = _conflicts.length;
29301 for (var i = 0; i < children.length; i++) {
29302 var id = children[i];
29303 var node = graph.hasEntity(id); // remove unused childNodes..
29305 if (targetWay.nodes.indexOf(id) === -1) {
29306 if (node && !isUsed(node, targetWay)) {
29307 updates.removeIds.push(id);
29311 } // restore used childNodes..
29314 var local = localGraph.hasEntity(id);
29315 var remote = remoteGraph.hasEntity(id);
29318 if (_option === 'force_remote' && remote && remote.visible) {
29319 updates.replacements.push(remote);
29320 } else if (_option === 'force_local' && local) {
29321 target = osmEntity(local);
29324 target = target.update({
29325 version: remote.version
29329 updates.replacements.push(target);
29330 } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
29331 target = osmEntity(local, {
29332 version: remote.version
29335 if (remote.visible) {
29336 target = mergeLocation(remote, target);
29338 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
29339 user: user(remote.user)
29343 if (_conflicts.length !== ccount) break;
29344 updates.replacements.push(target);
29351 function updateChildren(updates, graph) {
29352 for (var i = 0; i < updates.replacements.length; i++) {
29353 graph = graph.replace(updates.replacements[i]);
29356 if (updates.removeIds.length) {
29357 graph = actionDeleteMultiple(updates.removeIds)(graph);
29363 function mergeMembers(remote, target) {
29364 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
29368 if (_option === 'force_remote') {
29369 return target.update({
29370 members: remote.members
29374 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', {
29375 user: user(remote.user)
29381 function mergeTags(base, remote, target) {
29382 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
29386 if (_option === 'force_remote') {
29387 return target.update({
29392 var ccount = _conflicts.length;
29393 var o = base.tags || {};
29394 var a = target.tags || {};
29395 var b = remote.tags || {};
29396 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b)).filter(function (k) {
29397 return !discardTags[k];
29399 var tags = Object.assign({}, a); // shallow copy
29401 var changed = false;
29403 for (var i = 0; i < keys.length; i++) {
29406 if (o[k] !== b[k] && a[k] !== b[k]) {
29407 // changed remotely..
29408 if (o[k] !== a[k]) {
29409 // changed locally..
29410 _conflicts.push(_t('merge_remote_changes.conflict.tags', {
29414 user: user(remote.user)
29417 // unchanged locally, accept remote change..
29418 if (b.hasOwnProperty(k)) {
29429 return changed && _conflicts.length === ccount ? target.update({
29432 } // `graph.base()` is the common ancestor of the two graphs.
29433 // `localGraph` contains user's edits up to saving
29434 // `remoteGraph` contains remote edits to modified nodes
29435 // `graph` must be a descendent of `localGraph` and may include
29436 // some conflict resolution actions performed on it.
29438 // --- ... --- `localGraph` -- ... -- `graph`
29440 // `graph.base()` --- ... --- `remoteGraph`
29444 var action = function action(graph) {
29449 var base = graph.base().entities[id];
29450 var local = localGraph.entity(id);
29451 var remote = remoteGraph.entity(id);
29452 var target = osmEntity(local, {
29453 version: remote.version
29454 }); // delete/undelete
29456 if (!remote.visible) {
29457 if (_option === 'force_remote') {
29458 return actionDeleteMultiple([id])(graph);
29459 } else if (_option === 'force_local') {
29460 if (target.type === 'way') {
29461 target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
29462 graph = updateChildren(updates, graph);
29465 return graph.replace(target);
29467 _conflicts.push(_t('merge_remote_changes.conflict.deleted', {
29468 user: user(remote.user)
29471 return graph; // do nothing
29476 if (target.type === 'node') {
29477 target = mergeLocation(remote, target);
29478 } else if (target.type === 'way') {
29479 // pull in any child nodes that may not be present locally..
29480 graph.rebase(remoteGraph.childNodes(remote), [graph], false);
29481 target = mergeNodes(base, remote, target);
29482 target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
29483 } else if (target.type === 'relation') {
29484 target = mergeMembers(remote, target);
29487 target = mergeTags(base, remote, target);
29489 if (!_conflicts.length) {
29490 graph = updateChildren(updates, graph).replace(target);
29496 action.withOption = function (opt) {
29501 action.conflicts = function () {
29508 // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
29510 function actionMove(moveIDs, tryDelta, projection, cache) {
29511 var _delta = tryDelta;
29513 function setupCache(graph) {
29514 function canMove(nodeID) {
29515 // Allow movement of any node that is in the selectedIDs list..
29516 if (moveIDs.indexOf(nodeID) !== -1) return true; // Allow movement of a vertex where 2 ways meet..
29518 var parents = graph.parentWays(graph.entity(nodeID));
29519 if (parents.length < 3) return true; // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
29521 var parentsMoving = parents.every(function (way) {
29522 return cache.moving[way.id];
29524 if (!parentsMoving) delete cache.moving[nodeID];
29525 return parentsMoving;
29528 function cacheEntities(ids) {
29529 for (var i = 0; i < ids.length; i++) {
29531 if (cache.moving[id]) continue;
29532 cache.moving[id] = true;
29533 var entity = graph.hasEntity(id);
29534 if (!entity) continue;
29536 if (entity.type === 'node') {
29537 cache.nodes.push(id);
29538 cache.startLoc[id] = entity.loc;
29539 } else if (entity.type === 'way') {
29540 cache.ways.push(id);
29541 cacheEntities(entity.nodes);
29543 cacheEntities(entity.members.map(function (member) {
29550 function cacheIntersections(ids) {
29551 function isEndpoint(way, id) {
29552 return !way.isClosed() && !!way.affix(id);
29555 for (var i = 0; i < ids.length; i++) {
29556 var id = ids[i]; // consider only intersections with 1 moved and 1 unmoved way.
29558 var childNodes = graph.childNodes(graph.entity(id));
29560 for (var j = 0; j < childNodes.length; j++) {
29561 var node = childNodes[j];
29562 var parents = graph.parentWays(node);
29563 if (parents.length !== 2) continue;
29564 var moved = graph.entity(id);
29565 var unmoved = null;
29567 for (var k = 0; k < parents.length; k++) {
29568 var way = parents[k];
29570 if (!cache.moving[way.id]) {
29576 if (!unmoved) continue; // exclude ways that are overly connected..
29578 if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) continue;
29579 if (moved.isArea() || unmoved.isArea()) continue;
29580 cache.intersections.push({
29583 unmovedId: unmoved.id,
29584 movedIsEP: isEndpoint(moved, node.id),
29585 unmovedIsEP: isEndpoint(unmoved, node.id)
29597 cache.intersections = [];
29598 cache.replacedVertex = {};
29599 cache.startLoc = {};
29602 cacheEntities(moveIDs);
29603 cacheIntersections(cache.ways);
29604 cache.nodes = cache.nodes.filter(canMove);
29607 } // Place a vertex where the moved vertex used to be, to preserve way shape..
29616 // * node '*' added to preserve shape
29618 // / b ---- e way `b,e` moved here:
29625 function replaceMovedVertex(nodeId, wayId, graph, delta) {
29626 var way = graph.entity(wayId);
29627 var moved = graph.entity(nodeId);
29628 var movedIndex = way.nodes.indexOf(nodeId);
29629 var len, prevIndex, nextIndex;
29631 if (way.isClosed()) {
29632 len = way.nodes.length - 1;
29633 prevIndex = (movedIndex + len - 1) % len;
29634 nextIndex = (movedIndex + len + 1) % len;
29636 len = way.nodes.length;
29637 prevIndex = movedIndex - 1;
29638 nextIndex = movedIndex + 1;
29641 var prev = graph.hasEntity(way.nodes[prevIndex]);
29642 var next = graph.hasEntity(way.nodes[nextIndex]); // Don't add orig vertex at endpoint..
29644 if (!prev || !next) return graph;
29645 var key = wayId + '_' + nodeId;
29646 var orig = cache.replacedVertex[key];
29650 cache.replacedVertex[key] = orig;
29651 cache.startLoc[orig.id] = cache.startLoc[nodeId];
29657 start = projection(cache.startLoc[nodeId]);
29658 end = projection.invert(geoVecAdd(start, delta));
29660 end = cache.startLoc[nodeId];
29663 orig = orig.move(end);
29664 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..
29666 if (angle > 175 && angle < 185) return graph; // moving forward or backward along way?
29668 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
29669 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
29670 var d1 = geoPathLength(p1);
29671 var d2 = geoPathLength(p2);
29672 var insertAt = d1 <= d2 ? movedIndex : nextIndex; // moving around closed loop?
29674 if (way.isClosed() && insertAt === 0) insertAt = len;
29675 way = way.addNode(orig.id, insertAt);
29676 return graph.replace(orig).replace(way);
29677 } // Remove duplicate vertex that might have been added by
29678 // replaceMovedVertex. This is done after the unzorro checks.
29681 function removeDuplicateVertices(wayId, graph) {
29682 var way = graph.entity(wayId);
29683 var epsilon = 1e-6;
29686 function isInteresting(node, graph) {
29687 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
29690 for (var i = 0; i < way.nodes.length; i++) {
29691 curr = graph.entity(way.nodes[i]);
29693 if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
29694 if (!isInteresting(prev, graph)) {
29695 way = way.removeNode(prev.id);
29696 graph = graph.replace(way).remove(prev);
29697 } else if (!isInteresting(curr, graph)) {
29698 way = way.removeNode(curr.id);
29699 graph = graph.replace(way).remove(curr);
29707 } // Reorder nodes around intersections that have moved..
29709 // Start: way1.nodes: b,e (moving)
29710 // a - b - c ----- d way2.nodes: a,b,c,d (static)
29712 // e isEP1: true, isEP2, false
29714 // way1 `b,e` moved here:
29715 // a ----- c = b - d
29719 // reorder nodes way1.nodes: b,e
29720 // a ----- c - b - d way2.nodes: a,c,b,d
29726 function unZorroIntersection(intersection, graph) {
29727 var vertex = graph.entity(intersection.nodeId);
29728 var way1 = graph.entity(intersection.movedId);
29729 var way2 = graph.entity(intersection.unmovedId);
29730 var isEP1 = intersection.movedIsEP;
29731 var isEP2 = intersection.unmovedIsEP; // don't move the vertex if it is the endpoint of both ways.
29733 if (isEP1 && isEP2) return graph;
29734 var nodes1 = graph.childNodes(way1).filter(function (n) {
29735 return n !== vertex;
29737 var nodes2 = graph.childNodes(way2).filter(function (n) {
29738 return n !== vertex;
29740 if (way1.isClosed() && way1.first() === vertex.id) nodes1.push(nodes1[0]);
29741 if (way2.isClosed() && way2.first() === vertex.id) nodes2.push(nodes2[0]);
29742 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
29743 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
29744 var loc; // snap vertex to nearest edge (or some point between them)..
29746 if (!isEP1 && !isEP2) {
29747 var epsilon = 1e-6,
29750 for (var i = 0; i < maxIter; i++) {
29751 loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
29752 edge1 = geoChooseEdge(nodes1, projection(loc), projection);
29753 edge2 = geoChooseEdge(nodes2, projection(loc), projection);
29754 if (Math.abs(edge1.distance - edge2.distance) < epsilon) break;
29756 } else if (!isEP1) {
29762 graph = graph.replace(vertex.move(loc)); // if zorro happened, reorder nodes..
29764 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
29765 way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
29766 graph = graph.replace(way1);
29769 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
29770 way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
29771 graph = graph.replace(way2);
29777 function cleanupIntersections(graph) {
29778 for (var i = 0; i < cache.intersections.length; i++) {
29779 var obj = cache.intersections[i];
29780 graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
29781 graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
29782 graph = unZorroIntersection(obj, graph);
29783 graph = removeDuplicateVertices(obj.movedId, graph);
29784 graph = removeDuplicateVertices(obj.unmovedId, graph);
29788 } // check if moving way endpoint can cross an unmoved way, if so limit delta..
29791 function limitDelta(graph) {
29792 function moveNode(loc) {
29793 return geoVecAdd(projection(loc), _delta);
29796 for (var i = 0; i < cache.intersections.length; i++) {
29797 var obj = cache.intersections[i]; // Don't limit movement if this is vertex joins 2 endpoints..
29799 if (obj.movedIsEP && obj.unmovedIsEP) continue; // Don't limit movement if this vertex is not an endpoint anyway..
29801 if (!obj.movedIsEP) continue;
29802 var node = graph.entity(obj.nodeId);
29803 var start = projection(node.loc);
29804 var end = geoVecAdd(start, _delta);
29805 var movedNodes = graph.childNodes(graph.entity(obj.movedId));
29806 var movedPath = movedNodes.map(function (n) {
29807 return moveNode(n.loc);
29809 var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
29810 var unmovedPath = unmovedNodes.map(function (n) {
29811 return projection(n.loc);
29813 var hits = geoPathIntersections(movedPath, unmovedPath);
29815 for (var j = 0; i < hits.length; i++) {
29816 if (geoVecEqual(hits[j], end)) continue;
29817 var edge = geoChooseEdge(unmovedNodes, end, projection);
29818 _delta = geoVecSubtract(projection(edge.loc), start);
29823 var action = function action(graph) {
29824 if (_delta[0] === 0 && _delta[1] === 0) return graph;
29827 if (cache.intersections.length) {
29831 for (var i = 0; i < cache.nodes.length; i++) {
29832 var node = graph.entity(cache.nodes[i]);
29833 var start = projection(node.loc);
29834 var end = geoVecAdd(start, _delta);
29835 graph = graph.replace(node.move(projection.invert(end)));
29838 if (cache.intersections.length) {
29839 graph = cleanupIntersections(graph);
29845 action.delta = function () {
29852 function actionMoveMember(relationId, fromIndex, toIndex) {
29853 return function (graph) {
29854 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
29858 function actionMoveNode(nodeID, toLoc) {
29859 var action = function action(graph, t) {
29860 if (t === null || !isFinite(t)) t = 1;
29861 t = Math.min(Math.max(+t, 0), 1);
29862 var node = graph.entity(nodeID);
29863 return graph.replace(node.move(geoVecInterp(node.loc, toLoc, t)));
29866 action.transitionable = true;
29870 function actionNoop() {
29871 return function (graph) {
29876 function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
29877 var epsilon = ep || 1e-4;
29878 var threshold = degThresh || 13; // degrees within right or straight to alter
29879 // We test normalized dot products so we can compare as cos(angle)
29881 var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
29882 var upperThreshold = Math.cos(threshold * Math.PI / 180);
29884 var action = function action(graph, t) {
29885 if (t === null || !isFinite(t)) t = 1;
29886 t = Math.min(Math.max(+t, 0), 1);
29887 var way = graph.entity(wayID);
29888 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
29890 if (way.tags.nonsquare) {
29891 var tags = Object.assign({}, way.tags); // since we're squaring, remove indication that this is physically unsquare
29893 delete tags.nonsquare;
29899 graph = graph.replace(way);
29900 var isClosed = way.isClosed();
29901 var nodes = graph.childNodes(way).slice(); // shallow copy
29903 if (isClosed) nodes.pop();
29905 if (vertexID !== undefined) {
29906 nodes = nodeSubset(nodes, vertexID, isClosed);
29907 if (nodes.length !== 3) return graph;
29908 } // note: all geometry functions here use the unclosed node/point/coord list
29911 var nodeCount = {};
29917 var node, point, loc, score, motions, i, j;
29919 for (i = 0; i < nodes.length; i++) {
29921 nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
29924 coord: projection(node.loc)
29928 if (points.length === 3) {
29929 // move only one vertex for right triangle
29930 for (i = 0; i < 1000; i++) {
29931 motions = points.map(calcMotion);
29932 points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
29933 score = corner.dotp;
29935 if (score < epsilon) {
29940 node = graph.entity(nodes[corner.i].id);
29941 loc = projection.invert(points[corner.i].coord);
29942 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
29944 var straights = [];
29945 var simplified = []; // Remove points from nearly straight sections..
29946 // This produces a simplified shape to orthogonalize
29948 for (i = 0; i < points.length; i++) {
29952 if (isClosed || i > 0 && i < points.length - 1) {
29953 var a = points[(i - 1 + points.length) % points.length];
29954 var b = points[(i + 1) % points.length];
29955 dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
29958 if (dotp > upperThreshold) {
29959 straights.push(point);
29961 simplified.push(point);
29963 } // Orthogonalize the simplified shape
29966 var bestPoints = clonePoints(simplified);
29967 var originalPoints = clonePoints(simplified);
29970 for (i = 0; i < 1000; i++) {
29971 motions = simplified.map(calcMotion);
29973 for (j = 0; j < motions.length; j++) {
29974 simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
29977 var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
29979 if (newScore < score) {
29980 bestPoints = clonePoints(simplified);
29984 if (score < epsilon) {
29989 var bestCoords = bestPoints.map(function (p) {
29992 if (isClosed) bestCoords.push(bestCoords[0]); // move the nodes that should move
29994 for (i = 0; i < bestPoints.length; i++) {
29995 point = bestPoints[i];
29997 if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
29998 node = graph.entity(point.id);
29999 loc = projection.invert(point.coord);
30000 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
30002 } // move the nodes along straight segments
30005 for (i = 0; i < straights.length; i++) {
30006 point = straights[i];
30007 if (nodeCount[point.id] > 1) continue; // skip self-intersections
30009 node = graph.entity(point.id);
30011 if (t === 1 && graph.parentWays(node).length === 1 && graph.parentRelations(node).length === 0 && !node.hasInterestingTags()) {
30012 // remove uninteresting points..
30013 graph = actionDeleteNode(node.id)(graph);
30015 // move interesting points to the nearest edge..
30016 var choice = geoVecProject(point.coord, bestCoords);
30019 loc = projection.invert(choice.target);
30020 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
30028 function clonePoints(array) {
30029 return array.map(function (p) {
30032 coord: [p.coord[0], p.coord[1]]
30037 function calcMotion(point, i, array) {
30038 // don't try to move the endpoints of a non-closed way.
30039 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)
30041 if (nodeCount[array[i].id] > 1) return [0, 0];
30042 var a = array[(i - 1 + array.length) % array.length].coord;
30043 var origin = point.coord;
30044 var b = array[(i + 1) % array.length].coord;
30045 var p = geoVecSubtract(a, origin);
30046 var q = geoVecSubtract(b, origin);
30047 var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
30048 p = geoVecNormalize(p);
30049 q = geoVecNormalize(q);
30050 var dotp = p[0] * q[0] + p[1] * q[1];
30051 var val = Math.abs(dotp);
30053 if (val < lowerThreshold) {
30054 // nearly orthogonal
30057 var vec = geoVecNormalize(geoVecAdd(p, q));
30058 return geoVecScale(vec, 0.1 * dotp * scale);
30061 return [0, 0]; // do nothing
30063 }; // if we are only orthogonalizing one vertex,
30064 // get that vertex and the previous and next
30067 function nodeSubset(nodes, vertexID, isClosed) {
30068 var first = isClosed ? 0 : 1;
30069 var last = isClosed ? nodes.length : nodes.length - 1;
30071 for (var i = first; i < last; i++) {
30072 if (nodes[i].id === vertexID) {
30073 return [nodes[(i - 1 + nodes.length) % nodes.length], nodes[i], nodes[(i + 1) % nodes.length]];
30080 action.disabled = function (graph) {
30081 var way = graph.entity(wayID);
30082 way = way.removeNode(''); // sanity check - remove any consecutive duplicates
30084 graph = graph.replace(way);
30085 var isClosed = way.isClosed();
30086 var nodes = graph.childNodes(way).slice(); // shallow copy
30088 if (isClosed) nodes.pop();
30089 var allowStraightAngles = false;
30091 if (vertexID !== undefined) {
30092 allowStraightAngles = true;
30093 nodes = nodeSubset(nodes, vertexID, isClosed);
30094 if (nodes.length !== 3) return 'end_vertex';
30097 var coords = nodes.map(function (n) {
30098 return projection(n.loc);
30100 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
30102 if (score === null) {
30103 return 'not_squarish';
30104 } else if (score === 0) {
30105 return 'square_enough';
30111 action.transitionable = true;
30116 // `turn` must be an `osmTurn` object
30117 // see osm/intersection.js, pathToTurn()
30119 // This specifies a restriction of type `restriction` when traveling from
30120 // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
30121 // (The action does not check that these entities form a valid intersection.)
30123 // From, to, and via ways should be split before calling this action.
30124 // (old versions of the code would split the ways here, but we no longer do it)
30126 // For testing convenience, accepts a restrictionID to assign to the new
30127 // relation. Normally, this will be undefined and the relation will
30128 // automatically be assigned a new ID.
30131 function actionRestrictTurn(turn, restrictionType, restrictionID) {
30132 return function (graph) {
30133 var fromWay = graph.entity(turn.from.way);
30134 var toWay = graph.entity(turn.to.way);
30135 var viaNode = turn.via.node && graph.entity(turn.via.node);
30136 var viaWays = turn.via.ways && turn.via.ways.map(function (id) {
30137 return graph.entity(id);
30152 } else if (viaWays) {
30153 viaWays.forEach(function (viaWay) {
30167 return graph.replace(osmRelation({
30170 type: 'restriction',
30171 restriction: restrictionType
30178 function actionRevert(id) {
30179 var action = function action(graph) {
30180 var entity = graph.hasEntity(id),
30181 base = graph.base().entities[id];
30183 if (entity && !base) {
30184 // entity will be removed..
30185 if (entity.type === 'node') {
30186 graph.parentWays(entity).forEach(function (parent) {
30187 parent = parent.removeNode(id);
30188 graph = graph.replace(parent);
30190 if (parent.isDegenerate()) {
30191 graph = actionDeleteWay(parent.id)(graph);
30196 graph.parentRelations(entity).forEach(function (parent) {
30197 parent = parent.removeMembersWithID(id);
30198 graph = graph.replace(parent);
30200 if (parent.isDegenerate()) {
30201 graph = actionDeleteRelation(parent.id)(graph);
30206 return graph.revert(id);
30212 function actionRotate(rotateIds, pivot, angle, projection) {
30213 var action = function action(graph) {
30214 return graph.update(function (graph) {
30215 utilGetAllNodes(rotateIds, graph).forEach(function (node) {
30216 var point = geoRotate([projection(node.loc)], angle, pivot)[0];
30217 graph = graph.replace(node.move(projection.invert(point)));
30225 function actionScale(ids, pivotLoc, scaleFactor, projection) {
30226 return function (graph) {
30227 return graph.update(function (graph) {
30229 utilGetAllNodes(ids, graph).forEach(function (node) {
30230 point = projection(node.loc);
30231 radial = [point[0] - pivotLoc[0], point[1] - pivotLoc[1]];
30232 point = [pivotLoc[0] + scaleFactor * radial[0], pivotLoc[1] + scaleFactor * radial[1]];
30233 graph = graph.replace(node.move(projection.invert(point)));
30239 /* Align nodes along their common axis */
30241 function actionStraightenNodes(nodeIDs, projection) {
30242 function positionAlongWay(a, o, b) {
30243 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
30244 } // returns the endpoints of the long axis of symmetry of the `points` bounding rect
30247 function getEndpoints(points) {
30248 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
30249 // The shape's surrounding rectangle has 2 axes of symmetry.
30250 // Snap points to the long axis
30252 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
30253 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
30254 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
30255 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
30256 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
30265 var action = function action(graph, t) {
30266 if (t === null || !isFinite(t)) t = 1;
30267 t = Math.min(Math.max(+t, 0), 1);
30268 var nodes = nodeIDs.map(function (id) {
30269 return graph.entity(id);
30271 var points = nodes.map(function (n) {
30272 return projection(n.loc);
30274 var endpoints = getEndpoints(points);
30275 var startPoint = endpoints[0];
30276 var endPoint = endpoints[1]; // Move points onto the line connecting the endpoints
30278 for (var i = 0; i < points.length; i++) {
30279 var node = nodes[i];
30280 var point = points[i];
30281 var u = positionAlongWay(point, startPoint, endPoint);
30282 var point2 = geoVecInterp(startPoint, endPoint, u);
30283 var loc2 = projection.invert(point2);
30284 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
30290 action.disabled = function (graph) {
30291 var nodes = nodeIDs.map(function (id) {
30292 return graph.entity(id);
30294 var points = nodes.map(function (n) {
30295 return projection(n.loc);
30297 var endpoints = getEndpoints(points);
30298 var startPoint = endpoints[0];
30299 var endPoint = endpoints[1];
30300 var maxDistance = 0;
30302 for (var i = 0; i < points.length; i++) {
30303 var point = points[i];
30304 var u = positionAlongWay(point, startPoint, endPoint);
30305 var p = geoVecInterp(startPoint, endPoint, u);
30306 var dist = geoVecLength(p, point);
30308 if (!isNaN(dist) && dist > maxDistance) {
30309 maxDistance = dist;
30313 if (maxDistance < 0.0001) {
30314 return 'straight_enough';
30318 action.transitionable = true;
30323 * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
30326 function actionStraightenWay(selectedIDs, projection) {
30327 function positionAlongWay(a, o, b) {
30328 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
30329 } // Return all selected ways as a continuous, ordered array of nodes
30332 function allNodes(graph) {
30334 var startNodes = [];
30336 var remainingWays = [];
30337 var selectedWays = selectedIDs.filter(function (w) {
30338 return graph.entity(w).type === 'way';
30340 var selectedNodes = selectedIDs.filter(function (n) {
30341 return graph.entity(n).type === 'node';
30344 for (var i = 0; i < selectedWays.length; i++) {
30345 var way = graph.entity(selectedWays[i]);
30346 nodes = way.nodes.slice(0);
30347 remainingWays.push(nodes);
30348 startNodes.push(nodes[0]);
30349 endNodes.push(nodes[nodes.length - 1]);
30350 } // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
30351 // and need to be removed so currNode difference calculation below works)
30352 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
30355 startNodes = startNodes.filter(function (n) {
30356 return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
30358 endNodes = endNodes.filter(function (n) {
30359 return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
30360 }); // Choose the initial endpoint to start from
30362 var currNode = utilArrayDifference(startNodes, endNodes).concat(utilArrayDifference(endNodes, startNodes))[0];
30364 nodes = []; // Create nested function outside of loop to avoid "function in loop" lint error
30366 var getNextWay = function getNextWay(currNode, remainingWays) {
30367 return remainingWays.filter(function (way) {
30368 return way[0] === currNode || way[way.length - 1] === currNode;
30370 }; // Add nodes to end of nodes array, until all ways are added
30373 while (remainingWays.length) {
30374 nextWay = getNextWay(currNode, remainingWays);
30375 remainingWays = utilArrayDifference(remainingWays, [nextWay]);
30377 if (nextWay[0] !== currNode) {
30381 nodes = nodes.concat(nextWay);
30382 currNode = nodes[nodes.length - 1];
30383 } // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
30386 if (selectedNodes.length === 2) {
30387 var startNodeIdx = nodes.indexOf(selectedNodes[0]);
30388 var endNodeIdx = nodes.indexOf(selectedNodes[1]);
30389 var sortedStartEnd = [startNodeIdx, endNodeIdx];
30390 sortedStartEnd.sort(function (a, b) {
30393 nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1] + 1);
30396 return nodes.map(function (n) {
30397 return graph.entity(n);
30401 function shouldKeepNode(node, graph) {
30402 return graph.parentWays(node).length > 1 || graph.parentRelations(node).length || node.hasInterestingTags();
30405 var action = function action(graph, t) {
30406 if (t === null || !isFinite(t)) t = 1;
30407 t = Math.min(Math.max(+t, 0), 1);
30408 var nodes = allNodes(graph);
30409 var points = nodes.map(function (n) {
30410 return projection(n.loc);
30412 var startPoint = points[0];
30413 var endPoint = points[points.length - 1];
30417 for (i = 1; i < points.length - 1; i++) {
30418 var node = nodes[i];
30419 var point = points[i];
30421 if (t < 1 || shouldKeepNode(node, graph)) {
30422 var u = positionAlongWay(point, startPoint, endPoint);
30423 var p = geoVecInterp(startPoint, endPoint, u);
30424 var loc2 = projection.invert(p);
30425 graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
30428 if (toDelete.indexOf(node) === -1) {
30429 toDelete.push(node);
30434 for (i = 0; i < toDelete.length; i++) {
30435 graph = actionDeleteNode(toDelete[i].id)(graph);
30441 action.disabled = function (graph) {
30442 // check way isn't too bendy
30443 var nodes = allNodes(graph);
30444 var points = nodes.map(function (n) {
30445 return projection(n.loc);
30447 var startPoint = points[0];
30448 var endPoint = points[points.length - 1];
30449 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
30452 if (threshold === 0) {
30453 return 'too_bendy';
30456 var maxDistance = 0;
30458 for (i = 1; i < points.length - 1; i++) {
30459 var point = points[i];
30460 var u = positionAlongWay(point, startPoint, endPoint);
30461 var p = geoVecInterp(startPoint, endPoint, u);
30462 var dist = geoVecLength(p, point); // to bendy if point is off by 20% of total start/end distance in projected space
30464 if (isNaN(dist) || dist > threshold) {
30465 return 'too_bendy';
30466 } else if (dist > maxDistance) {
30467 maxDistance = dist;
30471 var keepingAllNodes = nodes.every(function (node, i) {
30472 return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
30475 if (maxDistance < 0.0001 && // Allow straightening even if already straight in order to remove extraneous nodes
30477 return 'straight_enough';
30481 action.transitionable = true;
30486 // `turn` must be an `osmTurn` object with a `restrictionID` property.
30487 // see osm/intersection.js, pathToTurn()
30490 function actionUnrestrictTurn(turn) {
30491 return function (graph) {
30492 return actionDeleteRelation(turn.restrictionID)(graph);
30496 /* Reflect the given area around its axis of symmetry */
30498 function actionReflect(reflectIds, projection) {
30499 var _useLongAxis = true;
30501 var action = function action(graph, t) {
30502 if (t === null || !isFinite(t)) t = 1;
30503 t = Math.min(Math.max(+t, 0), 1);
30504 var nodes = utilGetAllNodes(reflectIds, graph);
30505 var points = nodes.map(function (n) {
30506 return projection(n.loc);
30508 var ssr = geoGetSmallestSurroundingRectangle(points); // Choose line pq = axis of symmetry.
30509 // The shape's surrounding rectangle has 2 axes of symmetry.
30510 // Reflect across the longer axis by default.
30512 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2];
30513 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2];
30514 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2];
30515 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2];
30517 var isLong = geoVecLength(p1, q1) > geoVecLength(p2, q2);
30519 if (_useLongAxis && isLong || !_useLongAxis && !isLong) {
30525 } // reflect c across pq
30526 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
30529 var dx = q[0] - p[0];
30530 var dy = q[1] - p[1];
30531 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
30532 var b = 2 * dx * dy / (dx * dx + dy * dy);
30534 for (var i = 0; i < nodes.length; i++) {
30535 var node = nodes[i];
30536 var c = projection(node.loc);
30537 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]];
30538 var loc2 = projection.invert(c2);
30539 node = node.move(geoVecInterp(node.loc, loc2, t));
30540 graph = graph.replace(node);
30546 action.useLongAxis = function (val) {
30547 if (!arguments.length) return _useLongAxis;
30548 _useLongAxis = val;
30552 action.transitionable = true;
30556 function actionUpgradeTags(entityId, oldTags, replaceTags) {
30557 return function (graph) {
30558 var entity = graph.entity(entityId);
30559 var tags = Object.assign({}, entity.tags); // shallow copy
30564 for (var oldTagKey in oldTags) {
30565 if (!(oldTagKey in tags)) continue; // wildcard match
30567 if (oldTags[oldTagKey] === '*') {
30568 // note the value since we might need to transfer it
30569 transferValue = tags[oldTagKey];
30570 delete tags[oldTagKey]; // exact match
30571 } else if (oldTags[oldTagKey] === tags[oldTagKey]) {
30572 delete tags[oldTagKey]; // match is within semicolon-delimited values
30574 var vals = tags[oldTagKey].split(';').filter(Boolean);
30575 var oldIndex = vals.indexOf(oldTags[oldTagKey]);
30577 if (vals.length === 1 || oldIndex === -1) {
30578 delete tags[oldTagKey];
30580 if (replaceTags && replaceTags[oldTagKey]) {
30581 // replacing a value within a semicolon-delimited value, note the index
30582 semiIndex = oldIndex;
30585 vals.splice(oldIndex, 1);
30586 tags[oldTagKey] = vals.join(';');
30592 for (var replaceKey in replaceTags) {
30593 var replaceValue = replaceTags[replaceKey];
30595 if (replaceValue === '*') {
30596 if (tags[replaceKey] && tags[replaceKey] !== 'no') {
30597 // allow any pre-existing value except `no` (troll tag)
30600 // otherwise assume `yes` is okay
30601 tags[replaceKey] = 'yes';
30603 } else if (replaceValue === '$1') {
30604 tags[replaceKey] = transferValue;
30606 if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
30607 // don't override preexisting values
30608 var existingVals = tags[replaceKey].split(';').filter(Boolean);
30610 if (existingVals.indexOf(replaceValue) === -1) {
30611 existingVals.splice(semiIndex, 0, replaceValue);
30612 tags[replaceKey] = existingVals.join(';');
30615 tags[replaceKey] = replaceValue;
30621 return graph.replace(entity.update({
30627 function behaviorEdit(context) {
30628 function behavior() {
30629 context.map().minzoom(context.minEditableZoom());
30632 behavior.off = function () {
30633 context.map().minzoom(0);
30640 The hover behavior adds the `.hover` class on pointerover to all elements to which
30641 the identical datum is bound, and removes it on pointerout.
30643 The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
30644 representation may consist of several elements scattered throughout the DOM hierarchy.
30645 Only one of these elements can have the :hover pseudo-class, but all of them will
30646 have the .hover class.
30649 function behaviorHover(context) {
30650 var dispatch$1 = dispatch('hover');
30652 var _selection = select(null);
30654 var _newNodeId = null;
30655 var _initialNodeID = null;
30661 var _targets = []; // use pointer events on supported platforms; fallback to mouse events
30663 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
30665 function keydown(d3_event) {
30666 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
30667 _selection.selectAll('.hover').classed('hover-suppressed', true).classed('hover', false);
30669 _selection.classed('hover-disabled', true);
30671 dispatch$1.call('hover', this, null);
30675 function keyup(d3_event) {
30676 if (_altDisables && d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
30677 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false).classed('hover', true);
30679 _selection.classed('hover-disabled', false);
30681 dispatch$1.call('hover', this, _targets);
30685 function behavior(selection) {
30686 _selection = selection;
30689 if (_initialNodeID) {
30690 _newNodeId = _initialNodeID;
30691 _initialNodeID = null;
30696 _selection.on(_pointerPrefix + 'over.hover', pointerover).on(_pointerPrefix + 'out.hover', pointerout) // treat pointerdown as pointerover for touch devices
30697 .on(_pointerPrefix + 'down.hover', pointerover);
30699 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true).on('keydown.hover', keydown).on('keyup.hover', keyup);
30701 function eventTarget(d3_event) {
30702 var datum = d3_event.target && d3_event.target.__data__;
30703 if (_typeof(datum) !== 'object') return null;
30705 if (!(datum instanceof osmEntity) && datum.properties && datum.properties.entity instanceof osmEntity) {
30706 return datum.properties.entity;
30712 function pointerover(d3_event) {
30713 // ignore mouse hovers with buttons pressed unless dragging
30714 if (context.mode().id.indexOf('drag') === -1 && (!d3_event.pointerType || d3_event.pointerType === 'mouse') && d3_event.buttons) return;
30715 var target = eventTarget(d3_event);
30717 if (target && _targets.indexOf(target) === -1) {
30718 _targets.push(target);
30720 updateHover(d3_event, _targets);
30724 function pointerout(d3_event) {
30725 var target = eventTarget(d3_event);
30727 var index = _targets.indexOf(target);
30729 if (index !== -1) {
30730 _targets.splice(index);
30732 updateHover(d3_event, _targets);
30736 function allowsVertex(d) {
30737 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
30740 function modeAllowsHover(target) {
30741 var mode = context.mode();
30743 if (mode.id === 'add-point') {
30744 return mode.preset.matchGeometry('vertex') || target.type !== 'way' && target.geometry(context.graph()) !== 'vertex';
30750 function updateHover(d3_event, targets) {
30751 _selection.selectAll('.hover').classed('hover', false);
30753 _selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
30755 var mode = context.mode();
30757 if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
30758 var node = targets.find(function (target) {
30759 return target instanceof osmEntity && target.type === 'node';
30761 _newNodeId = node && node.id;
30764 targets = targets.filter(function (datum) {
30765 if (datum instanceof osmEntity) {
30766 // If drawing a way, don't hover on a node that was just placed. #3974
30767 return datum.id !== _newNodeId && (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) && modeAllowsHover(datum);
30774 for (var i in targets) {
30775 var datum = targets[i]; // What are we hovering over?
30777 if (datum.__featurehash__) {
30778 // hovering custom data
30779 selector += ', .data' + datum.__featurehash__;
30780 } else if (datum instanceof QAItem) {
30781 selector += ', .' + datum.service + '.itemId-' + datum.id;
30782 } else if (datum instanceof osmNote) {
30783 selector += ', .note-' + datum.id;
30784 } else if (datum instanceof osmEntity) {
30785 selector += ', .' + datum.id;
30787 if (datum.type === 'relation') {
30788 for (var j in datum.members) {
30789 selector += ', .' + datum.members[j].id;
30795 var suppressed = _altDisables && d3_event && d3_event.altKey;
30797 if (selector.trim().length) {
30798 // remove the first comma
30799 selector = selector.slice(1);
30801 _selection.selectAll(selector).classed(suppressed ? 'hover-suppressed' : 'hover', true);
30804 dispatch$1.call('hover', this, !suppressed && targets);
30808 behavior.off = function (selection) {
30809 selection.selectAll('.hover').classed('hover', false);
30810 selection.selectAll('.hover-suppressed').classed('hover-suppressed', false);
30811 selection.classed('hover-disabled', false);
30812 selection.on(_pointerPrefix + 'over.hover', null).on(_pointerPrefix + 'out.hover', null).on(_pointerPrefix + 'down.hover', null);
30813 select(window).on(_pointerPrefix + 'up.hover pointercancel.hover', null, true).on('keydown.hover', null).on('keyup.hover', null);
30816 behavior.altDisables = function (val) {
30817 if (!arguments.length) return _altDisables;
30818 _altDisables = val;
30822 behavior.ignoreVertex = function (val) {
30823 if (!arguments.length) return _ignoreVertex;
30824 _ignoreVertex = val;
30828 behavior.initialNodeID = function (nodeId) {
30829 _initialNodeID = nodeId;
30833 return utilRebind(behavior, dispatch$1, 'on');
30836 var _disableSpace = false;
30837 var _lastSpace = null;
30838 function behaviorDraw(context) {
30839 var dispatch$1 = dispatch('move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish');
30840 var keybinding = utilKeybinding('draw');
30842 var _hover = behaviorHover(context).altDisables(true).ignoreVertex(true).on('hover', context.ui().sidebar.hover);
30844 var _edit = behaviorEdit(context);
30846 var _closeTolerance = 4;
30847 var _tolerance = 12;
30848 var _mouseLeave = false;
30849 var _lastMouse = null;
30851 var _lastPointerUpEvent;
30853 var _downPointer; // use pointer events on supported platforms; fallback to mouse events
30856 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // related code
30857 // - `mode/drag_node.js` `datum()`
30860 function datum(d3_event) {
30861 var mode = context.mode();
30862 var isNote = mode && mode.id.indexOf('note') !== -1;
30863 if (d3_event.altKey || isNote) return {};
30866 if (d3_event.type === 'keydown') {
30867 element = _lastMouse && _lastMouse.target;
30869 element = d3_event.target;
30870 } // When drawing, snap only to touch targets..
30871 // (this excludes area fills and active drawing elements)
30874 var d = element.__data__;
30875 return d && d.properties && d.properties.target ? d : {};
30878 function pointerdown(d3_event) {
30879 if (_downPointer) return;
30880 var pointerLocGetter = utilFastMouse(this);
30882 id: d3_event.pointerId || 'mouse',
30883 pointerLocGetter: pointerLocGetter,
30884 downTime: +new Date(),
30885 downLoc: pointerLocGetter(d3_event)
30887 dispatch$1.call('down', this, d3_event, datum(d3_event));
30890 function pointerup(d3_event) {
30891 if (!_downPointer || _downPointer.id !== (d3_event.pointerId || 'mouse')) return;
30892 var downPointer = _downPointer;
30893 _downPointer = null;
30894 _lastPointerUpEvent = d3_event;
30895 if (downPointer.isCancelled) return;
30896 var t2 = +new Date();
30897 var p2 = downPointer.pointerLocGetter(d3_event);
30898 var dist = geoVecLength(downPointer.downLoc, p2);
30900 if (dist < _closeTolerance || dist < _tolerance && t2 - downPointer.downTime < 500) {
30901 // Prevent a quick second click
30902 select(window).on('click.draw-block', function () {
30903 d3_event.stopPropagation();
30905 context.map().dblclickZoomEnable(false);
30906 window.setTimeout(function () {
30907 context.map().dblclickZoomEnable(true);
30908 select(window).on('click.draw-block', null);
30910 click(d3_event, p2);
30914 function pointermove(d3_event) {
30915 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse') && !_downPointer.isCancelled) {
30916 var p2 = _downPointer.pointerLocGetter(d3_event);
30918 var dist = geoVecLength(_downPointer.downLoc, p2);
30920 if (dist >= _closeTolerance) {
30921 _downPointer.isCancelled = true;
30922 dispatch$1.call('downcancel', this);
30926 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
30927 // events immediately after non-mouse pointerup events; detect and ignore them.
30929 if (_lastPointerUpEvent && _lastPointerUpEvent.pointerType !== 'mouse' && d3_event.timeStamp - _lastPointerUpEvent.timeStamp < 100) return;
30930 _lastMouse = d3_event;
30931 dispatch$1.call('move', this, d3_event, datum(d3_event));
30934 function pointercancel(d3_event) {
30935 if (_downPointer && _downPointer.id === (d3_event.pointerId || 'mouse')) {
30936 if (!_downPointer.isCancelled) {
30937 dispatch$1.call('downcancel', this);
30940 _downPointer = null;
30944 function mouseenter() {
30945 _mouseLeave = false;
30948 function mouseleave() {
30949 _mouseLeave = true;
30952 function allowsVertex(d) {
30953 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
30955 // - `mode/drag_node.js` `doMove()`
30956 // - `behavior/draw.js` `click()`
30957 // - `behavior/draw_way.js` `move()`
30960 function click(d3_event, loc) {
30961 var d = datum(d3_event);
30962 var target = d && d.properties && d.properties.entity;
30963 var mode = context.mode();
30965 if (target && target.type === 'node' && allowsVertex(target)) {
30967 dispatch$1.call('clickNode', this, target, d);
30969 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {
30971 var choice = geoChooseEdge(context.graph().childNodes(target), loc, context.projection, context.activeID());
30974 var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
30975 dispatch$1.call('clickWay', this, choice.loc, edge, d);
30978 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
30979 var locLatLng = context.projection.invert(loc);
30980 dispatch$1.call('click', this, locLatLng, d);
30982 } // treat a spacebar press like a click
30985 function space(d3_event) {
30986 d3_event.preventDefault();
30987 d3_event.stopPropagation();
30988 var currSpace = context.map().mouse();
30990 if (_disableSpace && _lastSpace) {
30991 var dist = geoVecLength(_lastSpace, currSpace);
30993 if (dist > _tolerance) {
30994 _disableSpace = false;
30998 if (_disableSpace || _mouseLeave || !_lastMouse) return; // user must move mouse or release space bar to allow another click
31000 _lastSpace = currSpace;
31001 _disableSpace = true;
31002 select(window).on('keyup.space-block', function () {
31003 d3_event.preventDefault();
31004 d3_event.stopPropagation();
31005 _disableSpace = false;
31006 select(window).on('keyup.space-block', null);
31007 }); // get the current mouse position
31009 var loc = context.map().mouse() || // or the map center if the mouse has never entered the map
31010 context.projection(context.map().center());
31011 click(d3_event, loc);
31014 function backspace(d3_event) {
31015 d3_event.preventDefault();
31016 dispatch$1.call('undo');
31019 function del(d3_event) {
31020 d3_event.preventDefault();
31021 dispatch$1.call('cancel');
31024 function ret(d3_event) {
31025 d3_event.preventDefault();
31026 dispatch$1.call('finish');
31029 function behavior(selection) {
31030 context.install(_hover);
31031 context.install(_edit);
31032 _downPointer = null;
31033 keybinding.on('⌫', backspace).on('⌦', del).on('⎋', ret).on('↩', ret).on('space', space).on('⌥space', space);
31034 selection.on('mouseenter.draw', mouseenter).on('mouseleave.draw', mouseleave).on(_pointerPrefix + 'down.draw', pointerdown).on(_pointerPrefix + 'move.draw', pointermove);
31035 select(window).on(_pointerPrefix + 'up.draw', pointerup, true).on('pointercancel.draw', pointercancel, true);
31036 select(document).call(keybinding);
31040 behavior.off = function (selection) {
31041 context.ui().sidebar.hover.cancel();
31042 context.uninstall(_hover);
31043 context.uninstall(_edit);
31044 selection.on('mouseenter.draw', null).on('mouseleave.draw', null).on(_pointerPrefix + 'down.draw', null).on(_pointerPrefix + 'move.draw', null);
31045 select(window).on(_pointerPrefix + 'up.draw', null).on('pointercancel.draw', null); // note: keyup.space-block, click.draw-block should remain
31047 select(document).call(keybinding.unbind);
31050 behavior.hover = function () {
31054 return utilRebind(behavior, dispatch$1, 'on');
31057 function initRange(domain, range) {
31058 switch (arguments.length) {
31063 this.range(domain);
31067 this.range(range).domain(domain);
31074 function constants(x) {
31075 return function () {
31080 function number$1(x) {
31085 function identity$3(x) {
31089 function normalize$1(a, b) {
31090 return (b -= a = +a) ? function (x) {
31091 return (x - a) / b;
31092 } : constants(isNaN(b) ? NaN : 0.5);
31095 function clamper(a, b) {
31097 if (a > b) t = a, a = b, b = t;
31098 return function (x) {
31099 return Math.max(a, Math.min(b, x));
31101 } // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
31102 // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
31105 function bimap(domain, range, interpolate) {
31106 var d0 = domain[0],
31110 if (d1 < d0) d0 = normalize$1(d1, d0), r0 = interpolate(r1, r0);else d0 = normalize$1(d0, d1), r0 = interpolate(r0, r1);
31111 return function (x) {
31116 function polymap(domain, range, interpolate) {
31117 var j = Math.min(domain.length, range.length) - 1,
31120 i = -1; // Reverse descending domains.
31122 if (domain[j] < domain[0]) {
31123 domain = domain.slice().reverse();
31124 range = range.slice().reverse();
31128 d[i] = normalize$1(domain[i], domain[i + 1]);
31129 r[i] = interpolate(range[i], range[i + 1]);
31132 return function (x) {
31133 var i = bisectRight(domain, x, 1, j) - 1;
31134 return r[i](d[i](x));
31138 function copy(source, target) {
31139 return target.domain(source.domain()).range(source.range()).interpolate(source.interpolate()).clamp(source.clamp()).unknown(source.unknown());
31141 function transformer$1() {
31144 interpolate$1 = interpolate,
31148 clamp = identity$3,
31153 function rescale() {
31154 var n = Math.min(domain.length, range.length);
31155 if (clamp !== identity$3) clamp = clamper(domain[0], domain[n - 1]);
31156 piecewise = n > 2 ? polymap : bimap;
31157 output = input = null;
31161 function scale(x) {
31162 return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x)));
31165 scale.invert = function (y) {
31166 return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
31169 scale.domain = function (_) {
31170 return arguments.length ? (domain = Array.from(_, number$1), rescale()) : domain.slice();
31173 scale.range = function (_) {
31174 return arguments.length ? (range = Array.from(_), rescale()) : range.slice();
31177 scale.rangeRound = function (_) {
31178 return range = Array.from(_), interpolate$1 = interpolateRound, rescale();
31181 scale.clamp = function (_) {
31182 return arguments.length ? (clamp = _ ? true : identity$3, rescale()) : clamp !== identity$3;
31185 scale.interpolate = function (_) {
31186 return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1;
31189 scale.unknown = function (_) {
31190 return arguments.length ? (unknown = _, scale) : unknown;
31193 return function (t, u) {
31194 transform = t, untransform = u;
31198 function continuous() {
31199 return transformer$1()(identity$3, identity$3);
31202 function formatDecimal (x) {
31203 return Math.abs(x = Math.round(x)) >= 1e21 ? x.toLocaleString("en").replace(/,/g, "") : x.toString(10);
31204 } // Computes the decimal coefficient and exponent of the specified number x with
31205 // significant digits p, where x is positive and p is in [1, 21] or undefined.
31206 // For example, formatDecimalParts(1.23) returns ["123", 0].
31208 function formatDecimalParts(x, p) {
31209 if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
31212 coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
31213 // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
31215 return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +x.slice(i + 1)];
31218 function exponent (x) {
31219 return x = formatDecimalParts(Math.abs(x)), x ? x[1] : NaN;
31222 function formatGroup (grouping, thousands) {
31223 return function (value, width) {
31224 var i = value.length,
31230 while (i > 0 && g > 0) {
31231 if (length + g + 1 > width) g = Math.max(1, width - length);
31232 t.push(value.substring(i -= g, i + g));
31233 if ((length += g + 1) > width) break;
31234 g = grouping[j = (j + 1) % grouping.length];
31237 return t.reverse().join(thousands);
31241 function formatNumerals (numerals) {
31242 return function (value) {
31243 return value.replace(/[0-9]/g, function (i) {
31244 return numerals[+i];
31249 // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
31250 var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
31251 function formatSpecifier(specifier) {
31252 if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
31254 return new FormatSpecifier({
31262 precision: match[8] && match[8].slice(1),
31267 formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
31269 function FormatSpecifier(specifier) {
31270 this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
31271 this.align = specifier.align === undefined ? ">" : specifier.align + "";
31272 this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
31273 this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
31274 this.zero = !!specifier.zero;
31275 this.width = specifier.width === undefined ? undefined : +specifier.width;
31276 this.comma = !!specifier.comma;
31277 this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
31278 this.trim = !!specifier.trim;
31279 this.type = specifier.type === undefined ? "" : specifier.type + "";
31282 FormatSpecifier.prototype.toString = function () {
31283 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;
31286 // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
31287 function formatTrim (s) {
31288 out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
31295 if (i0 === 0) i0 = i;
31300 if (!+s[i]) break out;
31301 if (i0 > 0) i0 = 0;
31306 return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
31309 // `thisNumberValue` abstract operation
31310 // https://tc39.github.io/ecma262/#sec-thisnumbervalue
31311 var thisNumberValue = function (value) {
31312 if (typeof value != 'number' && classofRaw(value) != 'Number') {
31313 throw TypeError('Incorrect invocation');
31318 // `String.prototype.repeat` method implementation
31319 // https://tc39.github.io/ecma262/#sec-string.prototype.repeat
31320 var stringRepeat = ''.repeat || function repeat(count) {
31321 var str = String(requireObjectCoercible(this));
31323 var n = toInteger(count);
31324 if (n < 0 || n == Infinity) throw RangeError('Wrong number of repetitions');
31325 for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) result += str;
31329 var nativeToFixed = 1.0.toFixed;
31330 var floor$6 = Math.floor;
31332 var pow$2 = function (x, n, acc) {
31333 return n === 0 ? acc : n % 2 === 1 ? pow$2(x, n - 1, acc * x) : pow$2(x * x, n / 2, acc);
31336 var log$2 = function (x) {
31339 while (x2 >= 4096) {
31349 var FORCED$c = nativeToFixed && (
31350 0.00008.toFixed(3) !== '0.000' ||
31351 0.9.toFixed(0) !== '1' ||
31352 1.255.toFixed(2) !== '1.25' ||
31353 1000000000000000128.0.toFixed(0) !== '1000000000000000128'
31354 ) || !fails(function () {
31355 // V8 ~ Android 4.3-
31356 nativeToFixed.call({});
31359 // `Number.prototype.toFixed` method
31360 // https://tc39.github.io/ecma262/#sec-number.prototype.tofixed
31361 _export({ target: 'Number', proto: true, forced: FORCED$c }, {
31362 // eslint-disable-next-line max-statements
31363 toFixed: function toFixed(fractionDigits) {
31364 var number = thisNumberValue(this);
31365 var fractDigits = toInteger(fractionDigits);
31366 var data = [0, 0, 0, 0, 0, 0];
31371 var multiply = function (n, c) {
31374 while (++index < 6) {
31375 c2 += n * data[index];
31376 data[index] = c2 % 1e7;
31377 c2 = floor$6(c2 / 1e7);
31381 var divide = function (n) {
31384 while (--index >= 0) {
31386 data[index] = floor$6(c / n);
31391 var dataToString = function () {
31394 while (--index >= 0) {
31395 if (s !== '' || index === 0 || data[index] !== 0) {
31396 var t = String(data[index]);
31397 s = s === '' ? t : s + stringRepeat.call('0', 7 - t.length) + t;
31402 if (fractDigits < 0 || fractDigits > 20) throw RangeError('Incorrect fraction digits');
31403 // eslint-disable-next-line no-self-compare
31404 if (number != number) return 'NaN';
31405 if (number <= -1e21 || number >= 1e21) return String(number);
31410 if (number > 1e-21) {
31411 e = log$2(number * pow$2(2, 69, 1)) - 69;
31412 z = e < 0 ? number * pow$2(2, -e, 1) : number / pow$2(2, e, 1);
31413 z *= 0x10000000000000;
31422 multiply(pow$2(10, j, 1), 0);
31431 result = dataToString();
31434 multiply(1 << -e, 0);
31435 result = dataToString() + stringRepeat.call('0', fractDigits);
31438 if (fractDigits > 0) {
31440 result = sign + (k <= fractDigits
31441 ? '0.' + stringRepeat.call('0', fractDigits - k) + result
31442 : result.slice(0, k - fractDigits) + '.' + result.slice(k - fractDigits));
31444 result = sign + result;
31449 var nativeToPrecision = 1.0.toPrecision;
31451 var FORCED$d = fails(function () {
31453 return nativeToPrecision.call(1, undefined) !== '1';
31454 }) || !fails(function () {
31455 // V8 ~ Android 4.3-
31456 nativeToPrecision.call({});
31459 // `Number.prototype.toPrecision` method
31460 // https://tc39.github.io/ecma262/#sec-number.prototype.toprecision
31461 _export({ target: 'Number', proto: true, forced: FORCED$d }, {
31462 toPrecision: function toPrecision(precision) {
31463 return precision === undefined
31464 ? nativeToPrecision.call(thisNumberValue(this))
31465 : nativeToPrecision.call(thisNumberValue(this), precision);
31469 var prefixExponent;
31470 function formatPrefixAuto (x, p) {
31471 var d = formatDecimalParts(x, p);
31472 if (!d) return x + "";
31473 var coefficient = d[0],
31475 i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
31476 n = coefficient.length;
31477 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!
31480 function formatRounded (x, p) {
31481 var d = formatDecimalParts(x, p);
31482 if (!d) return x + "";
31483 var coefficient = d[0],
31485 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");
31488 var formatTypes = {
31489 "%": function _(x, p) {
31490 return (x * 100).toFixed(p);
31492 "b": function b(x) {
31493 return Math.round(x).toString(2);
31495 "c": function c(x) {
31498 "d": formatDecimal,
31499 "e": function e(x, p) {
31500 return x.toExponential(p);
31502 "f": function f(x, p) {
31503 return x.toFixed(p);
31505 "g": function g(x, p) {
31506 return x.toPrecision(p);
31508 "o": function o(x) {
31509 return Math.round(x).toString(8);
31511 "p": function p(x, _p) {
31512 return formatRounded(x * 100, _p);
31514 "r": formatRounded,
31515 "s": formatPrefixAuto,
31516 "X": function X(x) {
31517 return Math.round(x).toString(16).toUpperCase();
31519 "x": function x(_x) {
31520 return Math.round(_x).toString(16);
31524 function identity$4 (x) {
31528 var map = Array.prototype.map,
31529 prefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];
31530 function formatLocale (locale) {
31531 var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map.call(locale.grouping, Number), locale.thousands + ""),
31532 currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
31533 currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
31534 decimal = locale.decimal === undefined ? "." : locale.decimal + "",
31535 numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map.call(locale.numerals, String)),
31536 percent = locale.percent === undefined ? "%" : locale.percent + "",
31537 minus = locale.minus === undefined ? "−" : locale.minus + "",
31538 nan = locale.nan === undefined ? "NaN" : locale.nan + "";
31540 function newFormat(specifier) {
31541 specifier = formatSpecifier(specifier);
31542 var fill = specifier.fill,
31543 align = specifier.align,
31544 sign = specifier.sign,
31545 symbol = specifier.symbol,
31546 zero = specifier.zero,
31547 width = specifier.width,
31548 comma = specifier.comma,
31549 precision = specifier.precision,
31550 trim = specifier.trim,
31551 type = specifier.type; // The "n" type is an alias for ",g".
31553 if (type === "n") comma = true, type = "g"; // The "" type, and any invalid type, is an alias for ".12~g".
31554 else if (!formatTypes[type]) precision === undefined && (precision = 12), trim = true, type = "g"; // If zero fill is specified, padding goes after sign and before digits.
31556 if (zero || fill === "0" && align === "=") zero = true, fill = "0", align = "="; // Compute the prefix and suffix.
31557 // For SI-prefix, the suffix is lazily computed.
31559 var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
31560 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; // What format function should we use?
31561 // Is this an integer type?
31562 // Can this type generate exponential notation?
31564 var formatType = formatTypes[type],
31565 maybeSuffix = /[defgprs%]/.test(type); // Set the default precision if not specified,
31566 // or clamp the specified precision to the supported range.
31567 // For significant precision, it must be in [1, 21].
31568 // For fixed precision, it must be in [0, 20].
31570 precision = precision === undefined ? 6 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision)) : Math.max(0, Math.min(20, precision));
31572 function format(value) {
31573 var valuePrefix = prefix,
31574 valueSuffix = suffix,
31579 if (type === "c") {
31580 valueSuffix = formatType(value) + valueSuffix;
31583 value = +value; // Determine the sign. -0 is not less than 0, but 1 / -0 is!
31585 var valueNegative = value < 0 || 1 / value < 0; // Perform the initial formatting.
31587 value = isNaN(value) ? nan : formatType(Math.abs(value), precision); // Trim insignificant zeros.
31589 if (trim) value = formatTrim(value); // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
31591 if (valueNegative && +value === 0 && sign !== "+") valueNegative = false; // Compute the prefix and suffix.
31593 valuePrefix = (valueNegative ? sign === "(" ? sign : minus : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
31594 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be
31595 // grouped, and fractional or exponential “suffix” part that is not.
31598 i = -1, n = value.length;
31601 if (c = value.charCodeAt(i), 48 > c || c > 57) {
31602 valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
31603 value = value.slice(0, i);
31608 } // If the fill character is not "0", grouping is applied before padding.
31611 if (comma && !zero) value = group(value, Infinity); // Compute the padding.
31613 var length = valuePrefix.length + value.length + valueSuffix.length,
31614 padding = length < width ? new Array(width - length + 1).join(fill) : ""; // If the fill character is "0", grouping is applied after padding.
31616 if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; // Reconstruct the final output based on the desired alignment.
31620 value = valuePrefix + value + valueSuffix + padding;
31624 value = valuePrefix + padding + value + valueSuffix;
31628 value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
31632 value = padding + valuePrefix + value + valueSuffix;
31636 return numerals(value);
31639 format.toString = function () {
31640 return specifier + "";
31646 function formatPrefix(specifier, value) {
31647 var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
31648 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
31649 k = Math.pow(10, -e),
31650 prefix = prefixes[8 + e / 3];
31651 return function (value) {
31652 return f(k * value) + prefix;
31658 formatPrefix: formatPrefix
31668 currency: ["$", ""]
31670 function defaultLocale(definition) {
31671 locale = formatLocale(definition);
31672 format = locale.format;
31673 formatPrefix = locale.formatPrefix;
31677 function precisionFixed (step) {
31678 return Math.max(0, -exponent(Math.abs(step)));
31681 function precisionPrefix (step, value) {
31682 return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
31685 function precisionRound (step, max) {
31686 step = Math.abs(step), max = Math.abs(max) - step;
31687 return Math.max(0, exponent(max) - exponent(step)) + 1;
31690 function tickFormat(start, stop, count, specifier) {
31691 var step = tickStep(start, stop, count),
31693 specifier = formatSpecifier(specifier == null ? ",f" : specifier);
31695 switch (specifier.type) {
31698 var value = Math.max(Math.abs(start), Math.abs(stop));
31699 if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
31700 return formatPrefix(specifier, value);
31709 if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
31716 if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
31721 return format(specifier);
31724 function linearish(scale) {
31725 var domain = scale.domain;
31727 scale.ticks = function (count) {
31729 return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
31732 scale.tickFormat = function (count, specifier) {
31734 return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
31737 scale.nice = function (count) {
31738 if (count == null) count = 10;
31741 var i1 = d.length - 1;
31748 if (stop < start) {
31749 step = start, start = stop, stop = step;
31750 step = i0, i0 = i1, i1 = step;
31753 while (maxIter-- > 0) {
31754 step = tickIncrement(start, stop, count);
31756 if (step === prestep) {
31760 } else if (step > 0) {
31761 start = Math.floor(start / step) * step;
31762 stop = Math.ceil(stop / step) * step;
31763 } else if (step < 0) {
31764 start = Math.ceil(start * step) / step;
31765 stop = Math.floor(stop * step) / step;
31778 function linear$2() {
31779 var scale = continuous();
31781 scale.copy = function () {
31782 return copy(scale, linear$2());
31785 initRange.apply(scale, arguments);
31786 return linearish(scale);
31789 var nativeExpm1 = Math.expm1;
31790 var exp$1 = Math.exp;
31792 // `Math.expm1` method implementation
31793 // https://tc39.github.io/ecma262/#sec-math.expm1
31794 var mathExpm1 = (!nativeExpm1
31796 || nativeExpm1(10) > 22025.465794806719 || nativeExpm1(10) < 22025.4657948067165168
31798 || nativeExpm1(-2e-17) != -2e-17
31799 ) ? function expm1(x) {
31800 return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp$1(x) - 1;
31803 function quantize() {
31811 function scale(x) {
31812 return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
31815 function rescale() {
31817 domain = new Array(n);
31820 domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
31826 scale.domain = function (_) {
31829 return arguments.length ? ((_ref = _, _ref2 = _slicedToArray(_ref, 2), x0 = _ref2[0], x1 = _ref2[1], _ref), x0 = +x0, x1 = +x1, rescale()) : [x0, x1];
31832 scale.range = function (_) {
31833 return arguments.length ? (n = (range = Array.from(_)).length - 1, rescale()) : range.slice();
31836 scale.invertExtent = function (y) {
31837 var i = range.indexOf(y);
31838 return i < 0 ? [NaN, NaN] : i < 1 ? [x0, domain[0]] : i >= n ? [domain[n - 1], x1] : [domain[i - 1], domain[i]];
31841 scale.unknown = function (_) {
31842 return arguments.length ? (unknown = _, scale) : scale;
31845 scale.thresholds = function () {
31846 return domain.slice();
31849 scale.copy = function () {
31850 return quantize().domain([x0, x1]).range(range).unknown(unknown);
31853 return initRange.apply(linearish(scale), arguments);
31856 // https://github.com/tc39/proposal-string-pad-start-end
31861 var ceil$1 = Math.ceil;
31863 // `String.prototype.{ padStart, padEnd }` methods implementation
31864 var createMethod$6 = function (IS_END) {
31865 return function ($this, maxLength, fillString) {
31866 var S = String(requireObjectCoercible($this));
31867 var stringLength = S.length;
31868 var fillStr = fillString === undefined ? ' ' : String(fillString);
31869 var intMaxLength = toLength(maxLength);
31870 var fillLen, stringFiller;
31871 if (intMaxLength <= stringLength || fillStr == '') return S;
31872 fillLen = intMaxLength - stringLength;
31873 stringFiller = stringRepeat.call(fillStr, ceil$1(fillLen / fillStr.length));
31874 if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen);
31875 return IS_END ? S + stringFiller : stringFiller + S;
31880 // `String.prototype.padStart` method
31881 // https://tc39.github.io/ecma262/#sec-string.prototype.padstart
31882 start: createMethod$6(false),
31883 // `String.prototype.padEnd` method
31884 // https://tc39.github.io/ecma262/#sec-string.prototype.padend
31885 end: createMethod$6(true)
31888 var padStart = stringPad.start;
31890 var abs$3 = Math.abs;
31891 var DatePrototype$1 = Date.prototype;
31892 var getTime$1 = DatePrototype$1.getTime;
31893 var nativeDateToISOString = DatePrototype$1.toISOString;
31895 // `Date.prototype.toISOString` method implementation
31896 // https://tc39.github.io/ecma262/#sec-date.prototype.toisostring
31897 // PhantomJS / old WebKit fails here:
31898 var dateToIsoString = (fails(function () {
31899 return nativeDateToISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z';
31900 }) || !fails(function () {
31901 nativeDateToISOString.call(new Date(NaN));
31902 })) ? function toISOString() {
31903 if (!isFinite(getTime$1.call(this))) throw RangeError('Invalid time value');
31905 var year = date.getUTCFullYear();
31906 var milliseconds = date.getUTCMilliseconds();
31907 var sign = year < 0 ? '-' : year > 9999 ? '+' : '';
31908 return sign + padStart(abs$3(year), sign ? 6 : 4, 0) +
31909 '-' + padStart(date.getUTCMonth() + 1, 2, 0) +
31910 '-' + padStart(date.getUTCDate(), 2, 0) +
31911 'T' + padStart(date.getUTCHours(), 2, 0) +
31912 ':' + padStart(date.getUTCMinutes(), 2, 0) +
31913 ':' + padStart(date.getUTCSeconds(), 2, 0) +
31914 '.' + padStart(milliseconds, 3, 0) +
31916 } : nativeDateToISOString;
31918 // `Date.prototype.toISOString` method
31919 // https://tc39.github.io/ecma262/#sec-date.prototype.toisostring
31920 // PhantomJS / old WebKit has a broken implementations
31921 _export({ target: 'Date', proto: true, forced: Date.prototype.toISOString !== dateToIsoString }, {
31922 toISOString: dateToIsoString
31925 function behaviorBreathe() {
31926 var duration = 800;
31928 var selector = '.selected.shadow, .selected .shadow';
31930 var _selected = select(null);
31938 function ratchetyInterpolator(a, b, steps, units) {
31941 var sample = quantize().domain([0, 1]).range(d3_quantize(d3_interpolateNumber(a, b), steps));
31942 return function (t) {
31943 return String(sample(t)) + (units || '');
31947 function reset(selection) {
31948 selection.style('stroke-opacity', null).style('stroke-width', null).style('fill-opacity', null).style('r', null);
31951 function setAnimationParams(transition, fromTo) {
31952 var toFrom = fromTo === 'from' ? 'to' : 'from';
31953 transition.styleTween('stroke-opacity', function (d) {
31954 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
31955 }).styleTween('stroke-width', function (d) {
31956 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
31957 }).styleTween('fill-opacity', function (d) {
31958 return ratchetyInterpolator(_params[d.id][toFrom].opacity, _params[d.id][fromTo].opacity, steps);
31959 }).styleTween('r', function (d) {
31960 return ratchetyInterpolator(_params[d.id][toFrom].width, _params[d.id][fromTo].width, steps, 'px');
31964 function calcAnimationParams(selection) {
31965 selection.call(reset).each(function (d) {
31966 var s = select(this);
31967 var tag = s.node().tagName;
31973 var width; // determine base opacity and width
31975 if (tag === 'circle') {
31976 opacity = parseFloat(s.style('fill-opacity') || 0.5);
31977 width = parseFloat(s.style('r') || 15.5);
31979 opacity = parseFloat(s.style('stroke-opacity') || 0.7);
31980 width = parseFloat(s.style('stroke-width') || 10);
31981 } // calculate from/to interpolation params..
31985 p.from.opacity = opacity * 0.6;
31986 p.to.opacity = opacity * 1.25;
31987 p.from.width = width * 0.7;
31988 p.to.width = width * (tag === 'circle' ? 1.5 : 1);
31993 function run(surface, fromTo) {
31994 var toFrom = fromTo === 'from' ? 'to' : 'from';
31995 var currSelected = surface.selectAll(selector);
31996 var currClassed = surface.attr('class');
31998 if (_done || currSelected.empty()) {
31999 _selected.call(reset);
32001 _selected = select(null);
32005 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
32006 _selected.call(reset);
32008 _classed = currClassed;
32009 _selected = currSelected.call(calcAnimationParams);
32012 var didCallNextRun = false;
32014 _selected.transition().duration(duration).call(setAnimationParams, fromTo).on('end', function () {
32015 // `end` event is called for each selected element, but we want
32016 // it to run only once
32017 if (!didCallNextRun) {
32018 surface.call(run, toFrom);
32019 didCallNextRun = true;
32020 } // if entity was deselected, remove breathe styling
32023 if (!select(this).classed('selected')) {
32024 reset(select(this));
32029 function behavior(surface) {
32031 _timer = timer(function () {
32032 // wait for elements to actually become selected
32033 if (surface.selectAll(selector).empty()) {
32037 surface.call(run, 'from');
32045 behavior.restartIfNeeded = function (surface) {
32046 if (_selected.empty()) {
32047 surface.call(run, 'from');
32055 behavior.off = function () {
32062 _selected.interrupt().call(reset);
32068 /* Creates a keybinding behavior for an operation */
32069 function behaviorOperation(context) {
32072 function keypress(d3_event) {
32073 // prevent operations during low zoom selection
32074 if (!context.map().withinEditableZoom()) return;
32075 if (_operation.availableForKeypress && !_operation.availableForKeypress()) return;
32076 d3_event.preventDefault();
32078 var disabled = _operation.disabled();
32081 context.ui().flash.duration(4000).iconName('#iD-operation-' + _operation.id).iconClass('operation disabled').label(_operation.tooltip)();
32083 context.ui().flash.duration(2000).iconName('#iD-operation-' + _operation.id).iconClass('operation').label(_operation.annotation() || _operation.title)();
32084 if (_operation.point) _operation.point(null);
32090 function behavior() {
32091 if (_operation && _operation.available()) {
32092 context.keybinding().on(_operation.keys, keypress);
32098 behavior.off = function () {
32099 context.keybinding().off(_operation.keys);
32102 behavior.which = function (_) {
32103 if (!arguments.length) return _operation;
32111 function operationCircularize(context, selectedIDs) {
32114 var _actions = selectedIDs.map(getAction).filter(Boolean);
32116 var _amount = _actions.length === 1 ? 'single' : 'multiple';
32118 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
32122 function getAction(entityID) {
32123 var entity = context.entity(entityID);
32124 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) return null;
32127 _extent = entity.extent(context.graph());
32129 _extent = _extent.extend(entity.extent(context.graph()));
32132 return actionCircularize(entityID, context.projection);
32135 var operation = function operation() {
32136 if (!_actions.length) return;
32138 var combinedAction = function combinedAction(graph, t) {
32139 _actions.forEach(function (action) {
32140 if (!action.disabled(graph)) {
32141 graph = action(graph, t);
32148 combinedAction.transitionable = true;
32149 context.perform(combinedAction, operation.annotation());
32150 window.setTimeout(function () {
32151 context.validator().validate();
32152 }, 300); // after any transition
32155 operation.available = function () {
32156 return _actions.length && selectedIDs.length === _actions.length;
32157 }; // don't cache this because the visible extent could change
32160 operation.disabled = function () {
32161 if (!_actions.length) return '';
32163 var actionDisableds = _actions.map(function (action) {
32164 return action.disabled(context.graph());
32165 }).filter(Boolean);
32167 if (actionDisableds.length === _actions.length) {
32168 // none of the features can be circularized
32169 if (new Set(actionDisableds).size > 1) {
32170 return 'multiple_blockers';
32173 return actionDisableds[0];
32174 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
32175 return 'too_large';
32176 } else if (someMissing()) {
32177 return 'not_downloaded';
32178 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32179 return 'connected_to_hidden';
32184 function someMissing() {
32185 if (context.inIntro()) return false;
32186 var osm = context.connection();
32189 var missing = _coords.filter(function (loc) {
32190 return !osm.isDataLoaded(loc);
32193 if (missing.length) {
32194 missing.forEach(function (loc) {
32195 context.loadTileAtLoc(loc);
32205 operation.tooltip = function () {
32206 var disable = operation.disabled();
32207 return disable ? _t('operations.circularize.' + disable + '.' + _amount) : _t('operations.circularize.description.' + _amount);
32210 operation.annotation = function () {
32211 return _t('operations.circularize.annotation.feature', {
32216 operation.id = 'circularize';
32217 operation.keys = [_t('operations.circularize.key')];
32218 operation.title = _t('operations.circularize.title');
32219 operation.behavior = behaviorOperation(context).which(operation);
32223 // For example, ⌘Z -> Ctrl+Z
32225 var uiCmd = function uiCmd(code) {
32226 var detected = utilDetect();
32228 if (detected.os === 'mac') {
32232 if (detected.os === 'win') {
32233 if (code === '⌘⇧Z') return 'Ctrl+Y';
32245 for (var i = 0; i < code.length; i++) {
32246 if (code[i] in replacements) {
32247 result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
32254 }; // return a display-focused string for a given keyboard code
32256 uiCmd.display = function (code) {
32257 if (code.length !== 1) return code;
32258 var detected = utilDetect();
32259 var mac = detected.os === 'mac';
32260 var replacements = {
32261 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd') : _t('shortcuts.key.ctrl'),
32262 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift') : _t('shortcuts.key.shift'),
32263 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
32264 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl') : _t('shortcuts.key.ctrl'),
32265 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
32266 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del') : _t('shortcuts.key.del'),
32267 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup') : _t('shortcuts.key.pgup'),
32268 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn') : _t('shortcuts.key.pgdn'),
32269 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home') : _t('shortcuts.key.home'),
32270 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end') : _t('shortcuts.key.end'),
32271 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
32272 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc') : _t('shortcuts.key.esc'),
32273 '☰': mac ? '☰ ' + _t('shortcuts.key.menu') : _t('shortcuts.key.menu')
32275 return replacements[code] || code;
32278 function operationDelete(context, selectedIDs) {
32279 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32280 var action = actionDeleteMultiple(selectedIDs);
32281 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32282 var coords = nodes.map(function (n) {
32285 var extent = utilTotalExtent(selectedIDs, context.graph());
32287 var operation = function operation() {
32288 var nextSelectedID;
32289 var nextSelectedLoc;
32291 if (selectedIDs.length === 1) {
32292 var id = selectedIDs[0];
32293 var entity = context.entity(id);
32294 var geometry = entity.geometry(context.graph());
32295 var parents = context.graph().parentWays(entity);
32296 var parent = parents[0]; // Select the next closest node in the way.
32298 if (geometry === 'vertex') {
32299 var nodes = parent.nodes;
32300 var i = nodes.indexOf(id);
32304 } else if (i === nodes.length - 1) {
32307 var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
32308 var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
32309 i = a < b ? i - 1 : i + 1;
32312 nextSelectedID = nodes[i];
32313 nextSelectedLoc = context.entity(nextSelectedID).loc;
32317 context.perform(action, operation.annotation());
32318 context.validator().validate();
32320 if (nextSelectedID && nextSelectedLoc) {
32321 if (context.hasEntity(nextSelectedID)) {
32322 context.enter(modeSelect(context, [nextSelectedID]).follow(true));
32324 context.map().centerEase(nextSelectedLoc);
32325 context.enter(modeBrowse(context));
32328 context.enter(modeBrowse(context));
32332 operation.available = function () {
32336 operation.disabled = function () {
32337 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32338 return 'too_large';
32339 } else if (someMissing()) {
32340 return 'not_downloaded';
32341 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32342 return 'connected_to_hidden';
32343 } else if (selectedIDs.some(protectedMember)) {
32344 return 'part_of_relation';
32345 } else if (selectedIDs.some(incompleteRelation)) {
32346 return 'incomplete_relation';
32347 } else if (selectedIDs.some(hasWikidataTag)) {
32348 return 'has_wikidata_tag';
32353 function someMissing() {
32354 if (context.inIntro()) return false;
32355 var osm = context.connection();
32358 var missing = coords.filter(function (loc) {
32359 return !osm.isDataLoaded(loc);
32362 if (missing.length) {
32363 missing.forEach(function (loc) {
32364 context.loadTileAtLoc(loc);
32373 function hasWikidataTag(id) {
32374 var entity = context.entity(id);
32375 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
32378 function incompleteRelation(id) {
32379 var entity = context.entity(id);
32380 return entity.type === 'relation' && !entity.isComplete(context.graph());
32383 function protectedMember(id) {
32384 var entity = context.entity(id);
32385 if (entity.type !== 'way') return false;
32386 var parents = context.graph().parentRelations(entity);
32388 for (var i = 0; i < parents.length; i++) {
32389 var parent = parents[i];
32390 var type = parent.tags.type;
32391 var role = parent.memberById(id).role || 'outer';
32393 if (type === 'route' || type === 'boundary' || type === 'multipolygon' && role === 'outer') {
32402 operation.tooltip = function () {
32403 var disable = operation.disabled();
32404 return disable ? _t('operations.delete.' + disable + '.' + multi) : _t('operations.delete.description.' + multi);
32407 operation.annotation = function () {
32408 return selectedIDs.length === 1 ? _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.delete.annotation.feature', {
32409 n: selectedIDs.length
32413 operation.id = 'delete';
32414 operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
32415 operation.title = _t('operations.delete.title');
32416 operation.behavior = behaviorOperation(context).which(operation);
32420 function operationOrthogonalize(context, selectedIDs) {
32425 var _actions = selectedIDs.map(chooseAction).filter(Boolean);
32427 var _amount = _actions.length === 1 ? 'single' : 'multiple';
32429 var _coords = utilGetAllNodes(selectedIDs, context.graph()).map(function (n) {
32433 function chooseAction(entityID) {
32434 var entity = context.entity(entityID);
32435 var geometry = entity.geometry(context.graph());
32438 _extent = entity.extent(context.graph());
32440 _extent = _extent.extend(entity.extent(context.graph()));
32441 } // square a line/area
32444 if (entity.type === 'way' && new Set(entity.nodes).size > 2) {
32445 if (_type && _type !== 'feature') return null;
32447 return actionOrthogonalize(entityID, context.projection); // square a single vertex
32448 } else if (geometry === 'vertex') {
32449 if (_type && _type !== 'corner') return null;
32451 var graph = context.graph();
32452 var parents = graph.parentWays(entity);
32454 if (parents.length === 1) {
32455 var way = parents[0];
32457 if (way.nodes.indexOf(entityID) !== -1) {
32458 return actionOrthogonalize(way.id, context.projection, entityID);
32466 var operation = function operation() {
32467 if (!_actions.length) return;
32469 var combinedAction = function combinedAction(graph, t) {
32470 _actions.forEach(function (action) {
32471 if (!action.disabled(graph)) {
32472 graph = action(graph, t);
32479 combinedAction.transitionable = true;
32480 context.perform(combinedAction, operation.annotation());
32481 window.setTimeout(function () {
32482 context.validator().validate();
32483 }, 300); // after any transition
32486 operation.available = function () {
32487 return _actions.length && selectedIDs.length === _actions.length;
32488 }; // don't cache this because the visible extent could change
32491 operation.disabled = function () {
32492 if (!_actions.length) return '';
32494 var actionDisableds = _actions.map(function (action) {
32495 return action.disabled(context.graph());
32496 }).filter(Boolean);
32498 if (actionDisableds.length === _actions.length) {
32499 // none of the features can be squared
32500 if (new Set(actionDisableds).size > 1) {
32501 return 'multiple_blockers';
32504 return actionDisableds[0];
32505 } else if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
32506 return 'too_large';
32507 } else if (someMissing()) {
32508 return 'not_downloaded';
32509 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32510 return 'connected_to_hidden';
32515 function someMissing() {
32516 if (context.inIntro()) return false;
32517 var osm = context.connection();
32520 var missing = _coords.filter(function (loc) {
32521 return !osm.isDataLoaded(loc);
32524 if (missing.length) {
32525 missing.forEach(function (loc) {
32526 context.loadTileAtLoc(loc);
32536 operation.tooltip = function () {
32537 var disable = operation.disabled();
32538 return disable ? _t('operations.orthogonalize.' + disable + '.' + _amount) : _t('operations.orthogonalize.description.' + _type + '.' + _amount);
32541 operation.annotation = function () {
32542 return _t('operations.orthogonalize.annotation.' + _type, {
32547 operation.id = 'orthogonalize';
32548 operation.keys = [_t('operations.orthogonalize.key')];
32549 operation.title = _t('operations.orthogonalize.title');
32550 operation.behavior = behaviorOperation(context).which(operation);
32554 function operationReflectShort(context, selectedIDs) {
32555 return operationReflect(context, selectedIDs, 'short');
32557 function operationReflectLong(context, selectedIDs) {
32558 return operationReflect(context, selectedIDs, 'long');
32560 function operationReflect(context, selectedIDs, axis) {
32561 axis = axis || 'long';
32562 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32563 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32564 var coords = nodes.map(function (n) {
32567 var extent = utilTotalExtent(selectedIDs, context.graph());
32569 var operation = function operation() {
32570 var action = actionReflect(selectedIDs, context.projection).useLongAxis(Boolean(axis === 'long'));
32571 context.perform(action, operation.annotation());
32572 window.setTimeout(function () {
32573 context.validator().validate();
32574 }, 300); // after any transition
32577 operation.available = function () {
32578 return nodes.length >= 3;
32579 }; // don't cache this because the visible extent could change
32582 operation.disabled = function () {
32583 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32584 return 'too_large';
32585 } else if (someMissing()) {
32586 return 'not_downloaded';
32587 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32588 return 'connected_to_hidden';
32589 } else if (selectedIDs.some(incompleteRelation)) {
32590 return 'incomplete_relation';
32595 function someMissing() {
32596 if (context.inIntro()) return false;
32597 var osm = context.connection();
32600 var missing = coords.filter(function (loc) {
32601 return !osm.isDataLoaded(loc);
32604 if (missing.length) {
32605 missing.forEach(function (loc) {
32606 context.loadTileAtLoc(loc);
32615 function incompleteRelation(id) {
32616 var entity = context.entity(id);
32617 return entity.type === 'relation' && !entity.isComplete(context.graph());
32621 operation.tooltip = function () {
32622 var disable = operation.disabled();
32623 return disable ? _t('operations.reflect.' + disable + '.' + multi) : _t('operations.reflect.description.' + axis + '.' + multi);
32626 operation.annotation = function () {
32627 return _t('operations.reflect.annotation.' + axis + '.feature', {
32628 n: selectedIDs.length
32632 operation.id = 'reflect-' + axis;
32633 operation.keys = [_t('operations.reflect.key.' + axis)];
32634 operation.title = _t('operations.reflect.title.' + axis);
32635 operation.behavior = behaviorOperation(context).which(operation);
32639 function operationMove(context, selectedIDs) {
32640 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32641 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32642 var coords = nodes.map(function (n) {
32645 var extent = utilTotalExtent(selectedIDs, context.graph());
32647 var operation = function operation() {
32648 context.enter(modeMove(context, selectedIDs));
32651 operation.available = function () {
32652 return selectedIDs.length > 1 || context.entity(selectedIDs[0]).type !== 'node';
32655 operation.disabled = function () {
32656 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32657 return 'too_large';
32658 } else if (someMissing()) {
32659 return 'not_downloaded';
32660 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32661 return 'connected_to_hidden';
32662 } else if (selectedIDs.some(incompleteRelation)) {
32663 return 'incomplete_relation';
32668 function someMissing() {
32669 if (context.inIntro()) return false;
32670 var osm = context.connection();
32673 var missing = coords.filter(function (loc) {
32674 return !osm.isDataLoaded(loc);
32677 if (missing.length) {
32678 missing.forEach(function (loc) {
32679 context.loadTileAtLoc(loc);
32688 function incompleteRelation(id) {
32689 var entity = context.entity(id);
32690 return entity.type === 'relation' && !entity.isComplete(context.graph());
32694 operation.tooltip = function () {
32695 var disable = operation.disabled();
32696 return disable ? _t('operations.move.' + disable + '.' + multi) : _t('operations.move.description.' + multi);
32699 operation.annotation = function () {
32700 return selectedIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.move.annotation.feature', {
32701 n: selectedIDs.length
32705 operation.id = 'move';
32706 operation.keys = [_t('operations.move.key')];
32707 operation.title = _t('operations.move.title');
32708 operation.behavior = behaviorOperation(context).which(operation);
32709 operation.mouseOnly = true;
32713 function modeRotate(context, entityIDs) {
32718 var keybinding = utilKeybinding('rotate');
32719 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];
32720 var annotation = entityIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.rotate.annotation.feature', {
32721 n: entityIDs.length
32728 var _prevTransform;
32732 function doRotate() {
32735 if (context.graph() !== _prevGraph) {
32736 fn = context.perform;
32738 fn = context.replace;
32739 } // projection changed, recalculate _pivot
32742 var projection = context.projection;
32743 var currTransform = projection.transform();
32745 if (!_prevTransform || currTransform.k !== _prevTransform.k || currTransform.x !== _prevTransform.x || currTransform.y !== _prevTransform.y) {
32746 var nodes = utilGetAllNodes(entityIDs, context.graph());
32747 var points = nodes.map(function (n) {
32748 return projection(n.loc);
32750 _pivot = getPivot(points);
32751 _prevAngle = undefined;
32754 var currMouse = context.map().mouse();
32755 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
32756 if (typeof _prevAngle === 'undefined') _prevAngle = currAngle;
32757 var delta = currAngle - _prevAngle;
32758 fn(actionRotate(entityIDs, _pivot, delta, projection));
32759 _prevTransform = currTransform;
32760 _prevAngle = currAngle;
32761 _prevGraph = context.graph();
32764 function getPivot(points) {
32767 if (points.length === 1) {
32768 _pivot = points[0];
32769 } else if (points.length === 2) {
32770 _pivot = geoVecInterp(points[0], points[1], 0.5);
32772 var polygonHull = d3_polygonHull(points);
32774 if (polygonHull.length === 2) {
32775 _pivot = geoVecInterp(points[0], points[1], 0.5);
32777 _pivot = d3_polygonCentroid(d3_polygonHull(points));
32784 function finish(d3_event) {
32785 d3_event.stopPropagation();
32786 context.replace(actionNoop(), annotation);
32787 context.enter(modeSelect(context, entityIDs));
32790 function cancel() {
32792 context.enter(modeSelect(context, entityIDs));
32795 function undone() {
32796 context.enter(modeBrowse(context));
32799 mode.enter = function () {
32800 context.features().forceVisible(entityIDs);
32801 behaviors.forEach(context.install);
32802 context.surface().on('mousemove.rotate', doRotate).on('click.rotate', finish);
32803 context.history().on('undone.rotate', undone);
32804 keybinding.on('⎋', cancel).on('↩', finish);
32805 select(document).call(keybinding);
32808 mode.exit = function () {
32809 behaviors.forEach(context.uninstall);
32810 context.surface().on('mousemove.rotate', null).on('click.rotate', null);
32811 context.history().on('undone.rotate', null);
32812 select(document).call(keybinding.unbind);
32813 context.features().forceVisible([]);
32816 mode.selectedIDs = function () {
32817 if (!arguments.length) return entityIDs; // no assign
32825 function operationRotate(context, selectedIDs) {
32826 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
32827 var nodes = utilGetAllNodes(selectedIDs, context.graph());
32828 var coords = nodes.map(function (n) {
32831 var extent = utilTotalExtent(selectedIDs, context.graph());
32833 var operation = function operation() {
32834 context.enter(modeRotate(context, selectedIDs));
32837 operation.available = function () {
32838 return nodes.length >= 2;
32841 operation.disabled = function () {
32842 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
32843 return 'too_large';
32844 } else if (someMissing()) {
32845 return 'not_downloaded';
32846 } else if (selectedIDs.some(context.hasHiddenConnections)) {
32847 return 'connected_to_hidden';
32848 } else if (selectedIDs.some(incompleteRelation)) {
32849 return 'incomplete_relation';
32854 function someMissing() {
32855 if (context.inIntro()) return false;
32856 var osm = context.connection();
32859 var missing = coords.filter(function (loc) {
32860 return !osm.isDataLoaded(loc);
32863 if (missing.length) {
32864 missing.forEach(function (loc) {
32865 context.loadTileAtLoc(loc);
32874 function incompleteRelation(id) {
32875 var entity = context.entity(id);
32876 return entity.type === 'relation' && !entity.isComplete(context.graph());
32880 operation.tooltip = function () {
32881 var disable = operation.disabled();
32882 return disable ? _t('operations.rotate.' + disable + '.' + multi) : _t('operations.rotate.description.' + multi);
32885 operation.annotation = function () {
32886 return selectedIDs.length === 1 ? _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) : _t('operations.rotate.annotation.feature', {
32887 n: selectedIDs.length
32891 operation.id = 'rotate';
32892 operation.keys = [_t('operations.rotate.key')];
32893 operation.title = _t('operations.rotate.title');
32894 operation.behavior = behaviorOperation(context).which(operation);
32895 operation.mouseOnly = true;
32899 function modeMove(context, entityIDs, baseGraph) {
32904 var keybinding = utilKeybinding('move');
32905 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];
32906 var annotation = entityIDs.length === 1 ? _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) : _t('operations.move.annotation.feature', {
32907 n: entityIDs.length
32916 var _nudgeInterval;
32918 function doMove(nudge) {
32919 nudge = nudge || [0, 0];
32922 if (_prevGraph !== context.graph()) {
32924 _origin = context.map().mouseCoordinates();
32925 fn = context.perform;
32927 fn = context.overwrite;
32930 var currMouse = context.map().mouse();
32931 var origMouse = context.projection(_origin);
32932 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
32933 fn(actionMove(entityIDs, delta, context.projection, _cache));
32934 _prevGraph = context.graph();
32937 function startNudge(nudge) {
32938 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
32939 _nudgeInterval = window.setInterval(function () {
32940 context.map().pan(nudge);
32945 function stopNudge() {
32946 if (_nudgeInterval) {
32947 window.clearInterval(_nudgeInterval);
32948 _nudgeInterval = null;
32954 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
32963 function finish(d3_event) {
32964 d3_event.stopPropagation();
32965 context.replace(actionNoop(), annotation);
32966 context.enter(modeSelect(context, entityIDs));
32970 function cancel() {
32972 while (context.graph() !== baseGraph) {
32976 context.enter(modeBrowse(context));
32979 context.enter(modeSelect(context, entityIDs));
32985 function undone() {
32986 context.enter(modeBrowse(context));
32989 mode.enter = function () {
32990 _origin = context.map().mouseCoordinates();
32993 context.features().forceVisible(entityIDs);
32994 behaviors.forEach(context.install);
32995 context.surface().on('mousemove.move', move).on('click.move', finish);
32996 context.history().on('undone.move', undone);
32997 keybinding.on('⎋', cancel).on('↩', finish);
32998 select(document).call(keybinding);
33001 mode.exit = function () {
33003 behaviors.forEach(function (behavior) {
33004 context.uninstall(behavior);
33006 context.surface().on('mousemove.move', null).on('click.move', null);
33007 context.history().on('undone.move', null);
33008 select(document).call(keybinding.unbind);
33009 context.features().forceVisible([]);
33012 mode.selectedIDs = function () {
33013 if (!arguments.length) return entityIDs; // no assign
33021 function behaviorPaste(context) {
33022 function doPaste(d3_event) {
33023 // prevent paste during low zoom selection
33024 if (!context.map().withinEditableZoom()) return;
33025 d3_event.preventDefault();
33026 var baseGraph = context.graph();
33027 var mouse = context.map().mouse();
33028 var projection = context.projection;
33029 var viewport = geoExtent(projection.clipExtent()).polygon();
33030 if (!geoPointInPolygon(mouse, viewport)) return;
33031 var oldIDs = context.copyIDs();
33032 if (!oldIDs.length) return;
33033 var extent = geoExtent();
33034 var oldGraph = context.copyGraph();
33036 var action = actionCopyEntities(oldIDs, oldGraph);
33037 context.perform(action);
33038 var copies = action.copies();
33039 var originals = new Set();
33040 Object.values(copies).forEach(function (entity) {
33041 originals.add(entity.id);
33044 for (var id in copies) {
33045 var oldEntity = oldGraph.entity(id);
33046 var newEntity = copies[id];
33048 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
33051 var parents = context.graph().parentWays(newEntity);
33052 var parentCopied = parents.some(function (parent) {
33053 return originals.has(parent.id);
33056 if (!parentCopied) {
33057 newIDs.push(newEntity.id);
33059 } // Put pasted objects where mouse pointer is..
33062 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
33063 var delta = geoVecSubtract(mouse, copyPoint);
33064 context.perform(actionMove(newIDs, delta, projection));
33065 context.enter(modeMove(context, newIDs, baseGraph));
33068 function behavior() {
33069 context.keybinding().on(uiCmd('⌘V'), doPaste);
33073 behavior.off = function () {
33074 context.keybinding().off(uiCmd('⌘V'));
33080 // `String.prototype.repeat` method
33081 // https://tc39.github.io/ecma262/#sec-string.prototype.repeat
33082 _export({ target: 'String', proto: true }, {
33083 repeat: stringRepeat
33087 `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
33089 * The `origin` function is expected to return an [x, y] tuple rather than an
33091 * The events are `start`, `move`, and `end`.
33092 (https://github.com/mbostock/d3/issues/563)
33093 * The `start` event is not dispatched until the first cursor movement occurs.
33094 (https://github.com/mbostock/d3/pull/368)
33095 * The `move` event has a `point` and `delta` [x, y] tuple properties rather
33096 than `x`, `y`, `dx`, and `dy` properties.
33097 * The `end` event is not dispatched if no movement occurs.
33098 * An `off` function is available that unbinds the drag's internal event handlers.
33101 function behaviorDrag() {
33102 var dispatch$1 = dispatch('start', 'move', 'end'); // see also behaviorSelect
33104 var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
33106 var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
33108 var _origin = null;
33109 var _selector = '';
33117 var _pointerId; // use pointer events on supported platforms; fallback to mouse events
33120 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
33122 var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
33124 var d3_event_userSelectSuppress = function d3_event_userSelectSuppress() {
33125 var selection$1 = selection();
33126 var select = selection$1.style(d3_event_userSelectProperty);
33127 selection$1.style(d3_event_userSelectProperty, 'none');
33128 return function () {
33129 selection$1.style(d3_event_userSelectProperty, select);
33133 function pointerdown(d3_event) {
33134 if (_pointerId) return;
33135 _pointerId = d3_event.pointerId || 'mouse';
33136 _targetNode = this; // only force reflow once per drag
33138 var pointerLocGetter = utilFastMouse(_surface || _targetNode.parentNode);
33140 var startOrigin = pointerLocGetter(d3_event);
33141 var started = false;
33142 var selectEnable = d3_event_userSelectSuppress();
33143 select(window).on(_pointerPrefix + 'move.drag', pointermove).on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
33146 offset = _origin.call(_targetNode, _targetEntity);
33147 offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
33152 d3_event.stopPropagation();
33154 function pointermove(d3_event) {
33155 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
33156 var p = pointerLocGetter(d3_event);
33159 var dist = geoVecLength(startOrigin, p);
33160 var tolerance = d3_event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx; // don't start until the drag has actually moved somewhat
33162 if (dist < tolerance) return;
33164 dispatch$1.call('start', this, d3_event, _targetEntity); // Don't send a `move` event in the same cycle as `start` since dragging
33165 // a midpoint will convert the target to a node.
33168 d3_event.stopPropagation();
33169 d3_event.preventDefault();
33170 var dx = p[0] - startOrigin[0];
33171 var dy = p[1] - startOrigin[1];
33172 dispatch$1.call('move', this, d3_event, _targetEntity, [p[0] + offset[0], p[1] + offset[1]], [dx, dy]);
33176 function pointerup(d3_event) {
33177 if (_pointerId !== (d3_event.pointerId || 'mouse')) return;
33181 dispatch$1.call('end', this, d3_event, _targetEntity);
33182 d3_event.preventDefault();
33185 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
33190 function behavior(selection) {
33191 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
33192 var delegate = pointerdown;
33195 delegate = function delegate(d3_event) {
33197 var target = d3_event.target;
33199 for (; target && target !== root; target = target.parentNode) {
33200 var datum = target.__data__;
33201 _targetEntity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity;
33203 if (_targetEntity && target[matchesSelector](_selector)) {
33204 return pointerdown.call(target, d3_event);
33210 selection.on(_pointerPrefix + 'down.drag' + _selector, delegate);
33213 behavior.off = function (selection) {
33214 selection.on(_pointerPrefix + 'down.drag' + _selector, null);
33217 behavior.selector = function (_) {
33218 if (!arguments.length) return _selector;
33223 behavior.origin = function (_) {
33224 if (!arguments.length) return _origin;
33229 behavior.cancel = function () {
33230 select(window).on(_pointerPrefix + 'move.drag', null).on(_pointerPrefix + 'up.drag pointercancel.drag', null);
33234 behavior.targetNode = function (_) {
33235 if (!arguments.length) return _targetNode;
33240 behavior.targetEntity = function (_) {
33241 if (!arguments.length) return _targetEntity;
33246 behavior.surface = function (_) {
33247 if (!arguments.length) return _surface;
33252 return utilRebind(behavior, dispatch$1, 'on');
33255 function modeDragNode(context) {
33260 var hover = behaviorHover(context).altDisables(true).on('hover', context.ui().sidebar.hover);
33261 var edit = behaviorEdit(context);
33263 var _nudgeInterval;
33265 var _restoreSelectedIDs = [];
33266 var _wasMidpoint = false;
33267 var _isCancelled = false;
33275 function startNudge(d3_event, entity, nudge) {
33276 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
33277 _nudgeInterval = window.setInterval(function () {
33278 context.map().pan(nudge);
33279 doMove(d3_event, entity, nudge);
33283 function stopNudge() {
33284 if (_nudgeInterval) {
33285 window.clearInterval(_nudgeInterval);
33286 _nudgeInterval = null;
33290 function moveAnnotation(entity) {
33291 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
33294 function connectAnnotation(nodeEntity, targetEntity) {
33295 var nodeGeometry = nodeEntity.geometry(context.graph());
33296 var targetGeometry = targetEntity.geometry(context.graph());
33298 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
33299 var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
33300 var targetParentWayIDs = context.graph().parentWays(targetEntity);
33301 var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs); // if both vertices are part of the same way
33303 if (sharedParentWays.length !== 0) {
33304 // if the nodes are next to each other, they are merged
33305 if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
33306 return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
33309 return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
33313 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
33316 function shouldSnapToNode(target) {
33317 if (!_activeEntity) return false;
33318 return _activeEntity.geometry(context.graph()) !== 'vertex' || target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph());
33321 function origin(entity) {
33322 return context.projection(entity.loc);
33325 function keydown(d3_event) {
33326 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
33327 if (context.surface().classed('nope')) {
33328 context.surface().classed('nope-suppressed', true);
33331 context.surface().classed('nope', false).classed('nope-disabled', true);
33335 function keyup(d3_event) {
33336 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
33337 if (context.surface().classed('nope-suppressed')) {
33338 context.surface().classed('nope', true);
33341 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
33345 function start(d3_event, entity) {
33346 _wasMidpoint = entity.type === 'midpoint';
33347 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
33348 _isCancelled = !context.editable() || d3_event.shiftKey || hasHidden;
33350 if (_isCancelled) {
33352 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('modes.drag_node.connected_to_hidden'))();
33355 return drag.cancel();
33358 if (_wasMidpoint) {
33359 var midpoint = entity;
33360 entity = osmNode();
33361 context.perform(actionAddMidpoint(midpoint, entity));
33362 entity = context.entity(entity.id); // get post-action entity
33364 var vertex = context.surface().selectAll('.' + entity.id);
33365 drag.targetNode(vertex.node()).targetEntity(entity);
33367 context.perform(actionNoop());
33370 _activeEntity = entity;
33371 _startLoc = entity.loc;
33372 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
33373 context.surface().selectAll('.' + _activeEntity.id).classed('active', true);
33374 context.enter(mode);
33376 // - `behavior/draw.js` `datum()`
33379 function datum(d3_event) {
33380 if (!d3_event || d3_event.altKey) {
33383 // When dragging, snap only to touch targets..
33384 // (this excludes area fills and active drawing elements)
33385 var d = d3_event.target.__data__;
33386 return d && d.properties && d.properties.target ? d : {};
33390 function doMove(d3_event, entity, nudge) {
33391 nudge = nudge || [0, 0];
33392 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
33393 var currMouse = geoVecSubtract(currPoint, nudge);
33394 var loc = context.projection.invert(currMouse);
33397 if (!_nudgeInterval) {
33398 // If not nudging at the edge of the viewport, try to snap..
33400 // - `mode/drag_node.js` `doMove()`
33401 // - `behavior/draw.js` `click()`
33402 // - `behavior/draw_way.js` `move()`
33403 var d = datum(d3_event);
33404 target = d && d.properties && d.properties.entity;
33405 var targetLoc = target && target.loc;
33406 var targetNodes = d && d.properties && d.properties.nodes;
33409 // snap to node/vertex - a point target with `.loc`
33410 if (shouldSnapToNode(target)) {
33413 } else if (targetNodes) {
33414 // snap to way - a line target with `.nodes`
33415 edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
33423 context.replace(actionMoveNode(entity.id, loc)); // Below here: validations
33425 var isInvalid = false; // Check if this connection to `target` could cause relations to break..
33428 isInvalid = hasRelationConflict(entity, target, edge, context.graph());
33429 } // Check if this drag causes the geometry to break..
33433 isInvalid = hasInvalidGeometry(entity, context.graph());
33436 var nope = context.surface().classed('nope');
33438 if (isInvalid === 'relation' || isInvalid === 'restriction') {
33440 // about to nope - show hint
33441 context.ui().flash.duration(4000).iconName('#iD-icon-no').label(_t('operations.connect.' + isInvalid, {
33442 relation: _mainPresetIndex.item('type/restriction').name()
33445 } else if (isInvalid) {
33446 var errorID = isInvalid === 'line' ? 'lines' : 'areas';
33447 context.ui().flash.duration(3000).iconName('#iD-icon-no').label(_t('self_intersection.error.' + errorID))();
33450 // about to un-nope, remove hint
33451 context.ui().flash.duration(1).label('')();
33455 var nopeDisabled = context.surface().classed('nope-disabled');
33457 if (nopeDisabled) {
33458 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
33460 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
33464 } // Uses `actionConnect.disabled()` to know whether this connection is ok..
33467 function hasRelationConflict(entity, target, edge, graph) {
33468 var testGraph = graph.update(); // copy
33469 // if snapping to way - add midpoint there and consider that the target..
33472 var midpoint = osmNode();
33473 var action = actionAddMidpoint({
33475 edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
33477 testGraph = action(testGraph);
33479 } // can we connect to it?
33482 var ids = [entity.id, target.id];
33483 return actionConnect(ids).disabled(testGraph);
33486 function hasInvalidGeometry(entity, graph) {
33487 var parents = graph.parentWays(entity);
33490 for (i = 0; i < parents.length; i++) {
33491 var parent = parents[i];
33493 var activeIndex = null; // which multipolygon ring contains node being dragged
33494 // test any parent multipolygons for valid geometry
33496 var relations = graph.parentRelations(parent);
33498 for (j = 0; j < relations.length; j++) {
33499 if (!relations[j].isMultipolygon()) continue;
33500 var rings = osmJoinWays(relations[j].members, graph); // find active ring and test it for self intersections
33502 for (k = 0; k < rings.length; k++) {
33503 nodes = rings[k].nodes;
33505 if (nodes.find(function (n) {
33506 return n.id === entity.id;
33510 if (geoHasSelfIntersections(nodes, entity.id)) {
33511 return 'multipolygonMember';
33515 rings[k].coords = nodes.map(function (n) {
33518 } // test active ring for intersections with other rings in the multipolygon
33521 for (k = 0; k < rings.length; k++) {
33522 if (k === activeIndex) continue; // make sure active ring doesn't cross passive rings
33524 if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
33525 return 'multipolygonRing';
33528 } // If we still haven't tested this node's parent way for self-intersections.
33529 // (because it's not a member of a multipolygon), test it now.
33532 if (activeIndex === null) {
33533 nodes = parent.nodes.map(function (nodeID) {
33534 return graph.entity(nodeID);
33537 if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
33538 return parent.geometry(graph);
33546 function move(d3_event, entity, point) {
33547 if (_isCancelled) return;
33548 d3_event.stopPropagation();
33549 context.surface().classed('nope-disabled', d3_event.altKey);
33550 _lastLoc = context.projection.invert(point);
33551 doMove(d3_event, entity);
33552 var nudge = geoViewportEdge(point, context.map().dimensions());
33555 startNudge(d3_event, entity, nudge);
33561 function end(d3_event, entity) {
33562 if (_isCancelled) return;
33563 var wasPoint = entity.geometry(context.graph()) === 'point';
33564 var d = datum(d3_event);
33565 var nope = d && d.properties && d.properties.nope || context.surface().classed('nope');
33566 var target = d && d.properties && d.properties.entity; // entity to snap to
33570 context.perform(_actionBounceBack(entity.id, _startLoc));
33571 } else if (target && target.type === 'way') {
33572 var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
33573 context.replace(actionAddMidpoint({
33575 edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
33576 }, entity), connectAnnotation(entity, target));
33577 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
33578 context.replace(actionConnect([target.id, entity.id]), connectAnnotation(entity, target));
33579 } else if (_wasMidpoint) {
33580 context.replace(actionNoop(), _t('operations.add.annotation.vertex'));
33582 context.replace(actionNoop(), moveAnnotation(entity));
33586 context.enter(modeSelect(context, [entity.id]));
33588 var reselection = _restoreSelectedIDs.filter(function (id) {
33589 return context.graph().hasEntity(id);
33592 if (reselection.length) {
33593 context.enter(modeSelect(context, reselection));
33595 context.enter(modeBrowse(context));
33600 function _actionBounceBack(nodeID, toLoc) {
33601 var moveNode = actionMoveNode(nodeID, toLoc);
33603 var action = function action(graph, t) {
33604 // last time through, pop off the bounceback perform.
33605 // it will then overwrite the initial perform with a moveNode that does nothing
33606 if (t === 1) context.pop();
33607 return moveNode(graph, t);
33610 action.transitionable = true;
33614 function cancel() {
33616 context.enter(modeBrowse(context));
33619 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);
33621 mode.enter = function () {
33622 context.install(hover);
33623 context.install(edit);
33624 select(window).on('keydown.dragNode', keydown).on('keyup.dragNode', keyup);
33625 context.history().on('undone.drag-node', cancel);
33628 mode.exit = function () {
33629 context.ui().sidebar.hover.cancel();
33630 context.uninstall(hover);
33631 context.uninstall(edit);
33632 select(window).on('keydown.dragNode', null).on('keyup.dragNode', null);
33633 context.history().on('undone.drag-node', null);
33634 _activeEntity = null;
33635 context.surface().classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false).selectAll('.active').classed('active', false);
33639 mode.selectedIDs = function () {
33640 if (!arguments.length) return _activeEntity ? [_activeEntity.id] : []; // no assign
33645 mode.activeID = function () {
33646 if (!arguments.length) return _activeEntity && _activeEntity.id; // no assign
33651 mode.restoreSelectedIDs = function (_) {
33652 if (!arguments.length) return _restoreSelectedIDs;
33653 _restoreSelectedIDs = _;
33657 mode.behavior = drag;
33661 // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829
33662 var NON_GENERIC = !!nativePromiseConstructor && fails(function () {
33663 nativePromiseConstructor.prototype['finally'].call({ then: function () { /* empty */ } }, function () { /* empty */ });
33666 // `Promise.prototype.finally` method
33667 // https://tc39.github.io/ecma262/#sec-promise.prototype.finally
33668 _export({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, {
33669 'finally': function (onFinally) {
33670 var C = speciesConstructor(this, getBuiltIn('Promise'));
33671 var isFunction = typeof onFinally == 'function';
33673 isFunction ? function (x) {
33674 return promiseResolve(C, onFinally()).then(function () { return x; });
33676 isFunction ? function (e) {
33677 return promiseResolve(C, onFinally()).then(function () { throw e; });
33683 // patch native Promise.prototype for native async functions
33684 if ( typeof nativePromiseConstructor == 'function' && !nativePromiseConstructor.prototype['finally']) {
33685 redefine(nativePromiseConstructor.prototype, 'finally', getBuiltIn('Promise').prototype['finally']);
33689 fixRegexpWellKnownSymbolLogic('search', 1, function (SEARCH, nativeSearch, maybeCallNative) {
33691 // `String.prototype.search` method
33692 // https://tc39.github.io/ecma262/#sec-string.prototype.search
33693 function search(regexp) {
33694 var O = requireObjectCoercible(this);
33695 var searcher = regexp == undefined ? undefined : regexp[SEARCH];
33696 return searcher !== undefined ? searcher.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
33698 // `RegExp.prototype[@@search]` method
33699 // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@search
33700 function (regexp) {
33701 var res = maybeCallNative(nativeSearch, regexp, this);
33702 if (res.done) return res.value;
33704 var rx = anObject(regexp);
33705 var S = String(this);
33707 var previousLastIndex = rx.lastIndex;
33708 if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0;
33709 var result = regexpExecAbstract(rx, S);
33710 if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex;
33711 return result === null ? -1 : result.index;
33716 function quickselect$1(arr, k, left, right, compare) {
33717 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
33720 function quickselectStep(arr, k, left, right, compare) {
33721 while (right > left) {
33722 if (right - left > 600) {
33723 var n = right - left + 1;
33724 var m = k - left + 1;
33725 var z = Math.log(n);
33726 var s = 0.5 * Math.exp(2 * z / 3);
33727 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
33728 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
33729 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
33730 quickselectStep(arr, k, newLeft, newRight, compare);
33736 swap$1(arr, left, k);
33737 if (compare(arr[right], t) > 0) swap$1(arr, left, right);
33744 while (compare(arr[i], t) < 0) {
33748 while (compare(arr[j], t) > 0) {
33753 if (compare(arr[left], t) === 0) swap$1(arr, left, j);else {
33755 swap$1(arr, j, right);
33757 if (j <= k) left = j + 1;
33758 if (k <= j) right = j - 1;
33762 function swap$1(arr, i, j) {
33768 function defaultCompare(a, b) {
33769 return a < b ? -1 : a > b ? 1 : 0;
33772 var RBush = /*#__PURE__*/function () {
33774 var maxEntries = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 9;
33776 _classCallCheck(this, RBush);
33778 // max entries in a node is 9 by default; min node fill is 40% for best performance
33779 this._maxEntries = Math.max(4, maxEntries);
33780 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
33784 _createClass(RBush, [{
33786 value: function all() {
33787 return this._all(this.data, []);
33791 value: function search(bbox) {
33792 var node = this.data;
33794 if (!intersects(bbox, node)) return result;
33795 var toBBox = this.toBBox;
33796 var nodesToSearch = [];
33799 for (var i = 0; i < node.children.length; i++) {
33800 var child = node.children[i];
33801 var childBBox = node.leaf ? toBBox(child) : child;
33803 if (intersects(bbox, childBBox)) {
33804 if (node.leaf) result.push(child);else if (contains(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
33808 node = nodesToSearch.pop();
33815 value: function collides(bbox) {
33816 var node = this.data;
33817 if (!intersects(bbox, node)) return false;
33818 var nodesToSearch = [];
33821 for (var i = 0; i < node.children.length; i++) {
33822 var child = node.children[i];
33823 var childBBox = node.leaf ? this.toBBox(child) : child;
33825 if (intersects(bbox, childBBox)) {
33826 if (node.leaf || contains(bbox, childBBox)) return true;
33827 nodesToSearch.push(child);
33831 node = nodesToSearch.pop();
33838 value: function load(data) {
33839 if (!(data && data.length)) return this;
33841 if (data.length < this._minEntries) {
33842 for (var i = 0; i < data.length; i++) {
33843 this.insert(data[i]);
33847 } // recursively build the tree with the given data from scratch using OMT algorithm
33850 var node = this._build(data.slice(), 0, data.length - 1, 0);
33852 if (!this.data.children.length) {
33853 // save as is if tree is empty
33855 } else if (this.data.height === node.height) {
33856 // split root if trees have the same height
33857 this._splitRoot(this.data, node);
33859 if (this.data.height < node.height) {
33860 // swap trees if inserted one is bigger
33861 var tmpNode = this.data;
33864 } // insert the small tree into the large tree at appropriate level
33867 this._insert(node, this.data.height - node.height - 1, true);
33874 value: function insert(item) {
33875 if (item) this._insert(item, this.data.height - 1);
33880 value: function clear() {
33881 this.data = createNode([]);
33886 value: function remove(item, equalsFn) {
33887 if (!item) return this;
33888 var node = this.data;
33889 var bbox = this.toBBox(item);
33892 var i, parent, goingUp; // depth-first iterative tree traversal
33894 while (node || path.length) {
33898 parent = path[path.length - 1];
33904 // check current node
33905 var index = findItem(item, node.children, equalsFn);
33907 if (index !== -1) {
33908 // item found, remove the item and condense tree upwards
33909 node.children.splice(index, 1);
33912 this._condense(path);
33918 if (!goingUp && !node.leaf && contains(node, bbox)) {
33924 node = node.children[0];
33925 } else if (parent) {
33928 node = parent.children[i];
33930 } else node = null; // nothing found
33938 value: function toBBox(item) {
33942 key: "compareMinX",
33943 value: function compareMinX(a, b) {
33944 return a.minX - b.minX;
33947 key: "compareMinY",
33948 value: function compareMinY(a, b) {
33949 return a.minY - b.minY;
33953 value: function toJSON() {
33958 value: function fromJSON(data) {
33964 value: function _all(node, result) {
33965 var nodesToSearch = [];
33968 if (node.leaf) result.push.apply(result, _toConsumableArray(node.children));else nodesToSearch.push.apply(nodesToSearch, _toConsumableArray(node.children));
33969 node = nodesToSearch.pop();
33976 value: function _build(items, left, right, height) {
33977 var N = right - left + 1;
33978 var M = this._maxEntries;
33982 // reached leaf level; return leaf
33983 node = createNode(items.slice(left, right + 1));
33984 calcBBox(node, this.toBBox);
33989 // target height of the bulk-loaded tree
33990 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
33992 M = Math.ceil(N / Math.pow(M, height - 1));
33995 node = createNode([]);
33997 node.height = height; // split the items into M mostly square tiles
33999 var N2 = Math.ceil(N / M);
34000 var N1 = N2 * Math.ceil(Math.sqrt(M));
34001 multiSelect(items, left, right, N1, this.compareMinX);
34003 for (var i = left; i <= right; i += N1) {
34004 var right2 = Math.min(i + N1 - 1, right);
34005 multiSelect(items, i, right2, N2, this.compareMinY);
34007 for (var j = i; j <= right2; j += N2) {
34008 var right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
34010 node.children.push(this._build(items, j, right3, height - 1));
34014 calcBBox(node, this.toBBox);
34018 key: "_chooseSubtree",
34019 value: function _chooseSubtree(bbox, node, level, path) {
34022 if (node.leaf || path.length - 1 === level) break;
34023 var minArea = Infinity;
34024 var minEnlargement = Infinity;
34025 var targetNode = void 0;
34027 for (var i = 0; i < node.children.length; i++) {
34028 var child = node.children[i];
34029 var area = bboxArea(child);
34030 var enlargement = enlargedArea(bbox, child) - area; // choose entry with the least area enlargement
34032 if (enlargement < minEnlargement) {
34033 minEnlargement = enlargement;
34034 minArea = area < minArea ? area : minArea;
34035 targetNode = child;
34036 } else if (enlargement === minEnlargement) {
34037 // otherwise choose one with the smallest area
34038 if (area < minArea) {
34040 targetNode = child;
34045 node = targetNode || node.children[0];
34052 value: function _insert(item, level, isNode) {
34053 var bbox = isNode ? item : this.toBBox(item);
34054 var insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
34056 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
34059 node.children.push(item);
34060 extend$1(node, bbox); // split on node overflow; propagate upwards if necessary
34062 while (level >= 0) {
34063 if (insertPath[level].children.length > this._maxEntries) {
34064 this._split(insertPath, level);
34068 } // adjust bboxes along the insertion path
34071 this._adjustParentBBoxes(bbox, insertPath, level);
34072 } // split overflowed node into two
34076 value: function _split(insertPath, level) {
34077 var node = insertPath[level];
34078 var M = node.children.length;
34079 var m = this._minEntries;
34081 this._chooseSplitAxis(node, m, M);
34083 var splitIndex = this._chooseSplitIndex(node, m, M);
34085 var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
34086 newNode.height = node.height;
34087 newNode.leaf = node.leaf;
34088 calcBBox(node, this.toBBox);
34089 calcBBox(newNode, this.toBBox);
34090 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
34094 value: function _splitRoot(node, newNode) {
34096 this.data = createNode([node, newNode]);
34097 this.data.height = node.height + 1;
34098 this.data.leaf = false;
34099 calcBBox(this.data, this.toBBox);
34102 key: "_chooseSplitIndex",
34103 value: function _chooseSplitIndex(node, m, M) {
34105 var minOverlap = Infinity;
34106 var minArea = Infinity;
34108 for (var i = m; i <= M - m; i++) {
34109 var bbox1 = distBBox(node, 0, i, this.toBBox);
34110 var bbox2 = distBBox(node, i, M, this.toBBox);
34111 var overlap = intersectionArea(bbox1, bbox2);
34112 var area = bboxArea(bbox1) + bboxArea(bbox2); // choose distribution with minimum overlap
34114 if (overlap < minOverlap) {
34115 minOverlap = overlap;
34117 minArea = area < minArea ? area : minArea;
34118 } else if (overlap === minOverlap) {
34119 // otherwise choose distribution with minimum area
34120 if (area < minArea) {
34127 return index || M - m;
34128 } // sorts node children by the best axis for split
34131 key: "_chooseSplitAxis",
34132 value: function _chooseSplitAxis(node, m, M) {
34133 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
34134 var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
34136 var xMargin = this._allDistMargin(node, m, M, compareMinX);
34138 var yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
34139 // otherwise it's already sorted by minY
34142 if (xMargin < yMargin) node.children.sort(compareMinX);
34143 } // total margin of all possible split distributions where each node is at least m full
34146 key: "_allDistMargin",
34147 value: function _allDistMargin(node, m, M, compare) {
34148 node.children.sort(compare);
34149 var toBBox = this.toBBox;
34150 var leftBBox = distBBox(node, 0, m, toBBox);
34151 var rightBBox = distBBox(node, M - m, M, toBBox);
34152 var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
34154 for (var i = m; i < M - m; i++) {
34155 var child = node.children[i];
34156 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
34157 margin += bboxMargin(leftBBox);
34160 for (var _i = M - m - 1; _i >= m; _i--) {
34161 var _child = node.children[_i];
34162 extend$1(rightBBox, node.leaf ? toBBox(_child) : _child);
34163 margin += bboxMargin(rightBBox);
34169 key: "_adjustParentBBoxes",
34170 value: function _adjustParentBBoxes(bbox, path, level) {
34171 // adjust bboxes along the given tree path
34172 for (var i = level; i >= 0; i--) {
34173 extend$1(path[i], bbox);
34178 value: function _condense(path) {
34179 // go through the path, removing empty nodes and updating bboxes
34180 for (var i = path.length - 1, siblings; i >= 0; i--) {
34181 if (path[i].children.length === 0) {
34183 siblings = path[i - 1].children;
34184 siblings.splice(siblings.indexOf(path[i]), 1);
34185 } else this.clear();
34186 } else calcBBox(path[i], this.toBBox);
34194 function findItem(item, items, equalsFn) {
34195 if (!equalsFn) return items.indexOf(item);
34197 for (var i = 0; i < items.length; i++) {
34198 if (equalsFn(item, items[i])) return i;
34202 } // calculate node's bbox from bboxes of its children
34205 function calcBBox(node, toBBox) {
34206 distBBox(node, 0, node.children.length, toBBox, node);
34207 } // min bounding rectangle of node children from k to p-1
34210 function distBBox(node, k, p, toBBox, destNode) {
34211 if (!destNode) destNode = createNode(null);
34212 destNode.minX = Infinity;
34213 destNode.minY = Infinity;
34214 destNode.maxX = -Infinity;
34215 destNode.maxY = -Infinity;
34217 for (var i = k; i < p; i++) {
34218 var child = node.children[i];
34219 extend$1(destNode, node.leaf ? toBBox(child) : child);
34225 function extend$1(a, b) {
34226 a.minX = Math.min(a.minX, b.minX);
34227 a.minY = Math.min(a.minY, b.minY);
34228 a.maxX = Math.max(a.maxX, b.maxX);
34229 a.maxY = Math.max(a.maxY, b.maxY);
34233 function compareNodeMinX(a, b) {
34234 return a.minX - b.minX;
34237 function compareNodeMinY(a, b) {
34238 return a.minY - b.minY;
34241 function bboxArea(a) {
34242 return (a.maxX - a.minX) * (a.maxY - a.minY);
34245 function bboxMargin(a) {
34246 return a.maxX - a.minX + (a.maxY - a.minY);
34249 function enlargedArea(a, b) {
34250 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));
34253 function intersectionArea(a, b) {
34254 var minX = Math.max(a.minX, b.minX);
34255 var minY = Math.max(a.minY, b.minY);
34256 var maxX = Math.min(a.maxX, b.maxX);
34257 var maxY = Math.min(a.maxY, b.maxY);
34258 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
34261 function contains(a, b) {
34262 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
34265 function intersects(a, b) {
34266 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
34269 function createNode(children) {
34271 children: children,
34279 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
34280 // combines selection algorithm with binary divide & conquer approach
34283 function multiSelect(arr, left, right, n, compare) {
34284 var stack = [left, right];
34286 while (stack.length) {
34287 right = stack.pop();
34288 left = stack.pop();
34289 if (right - left <= n) continue;
34290 var mid = left + Math.ceil((right - left) / n / 2) * n;
34291 quickselect$1(arr, mid, left, right, compare);
34292 stack.push(left, mid, mid, right);
34296 var tiler = utilTiler();
34297 var dispatch$1 = dispatch('loaded');
34298 var _tileZoom = 14;
34299 var _krUrlRoot = 'https://www.keepright.at';
34302 localizeStrings: {}
34303 }; // This gets reassigned if reset
34307 var _krRuleset = [// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
34308 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];
34310 function abortRequest(controller) {
34312 controller.abort();
34316 function abortUnwantedRequests(cache, tiles) {
34317 Object.keys(cache.inflightTile).forEach(function (k) {
34318 var wanted = tiles.find(function (tile) {
34319 return k === tile.id;
34323 abortRequest(cache.inflightTile[k]);
34324 delete cache.inflightTile[k];
34329 function encodeIssueRtree(d) {
34337 } // Replace or remove QAItem from rtree
34340 function updateRtree(item, replace) {
34341 _cache.rtree.remove(item, function (a, b) {
34342 return a.data.id === b.data.id;
34346 _cache.rtree.insert(item);
34350 function tokenReplacements(d) {
34351 if (!(d instanceof QAItem)) return;
34352 var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
34353 var replacements = {};
34354 var issueTemplate = _krData.errorTypes[d.whichType];
34356 if (!issueTemplate) {
34357 /* eslint-disable no-console */
34358 console.log('No Template: ', d.whichType);
34359 console.log(' ', d.description);
34360 /* eslint-enable no-console */
34363 } // some descriptions are just fixed text
34366 if (!issueTemplate.regex) return; // regex pattern should match description with variable details captured
34368 var errorRegex = new RegExp(issueTemplate.regex, 'i');
34369 var errorMatch = errorRegex.exec(d.description);
34372 /* eslint-disable no-console */
34373 console.log('Unmatched: ', d.whichType);
34374 console.log(' ', d.description);
34375 console.log(' ', errorRegex);
34376 /* eslint-enable no-console */
34381 for (var i = 1; i < errorMatch.length; i++) {
34383 var capture = errorMatch[i];
34384 var idType = void 0;
34385 idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i - 1] : '';
34387 if (idType && capture) {
34388 // link IDs if present in the capture
34389 capture = parseError(capture, idType);
34390 } else if (htmlRegex.test(capture)) {
34391 // escape any html in non-IDs
34392 capture = '\\' + capture + '\\';
34394 var compare = capture.toLowerCase();
34396 if (_krData.localizeStrings[compare]) {
34397 // some replacement strings can be localized
34398 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
34402 replacements['var' + i] = capture;
34405 return replacements;
34408 function parseError(capture, idType) {
34409 var compare = capture.toLowerCase();
34411 if (_krData.localizeStrings[compare]) {
34412 // some replacement strings can be localized
34413 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
34417 // link a string like "this node"
34419 capture = linkErrorObject(capture);
34423 capture = linkURL(capture);
34425 // link an entity ID
34430 capture = linkEntity(idType + capture);
34432 // some errors have more complex ID lists/variance
34435 capture = parse20(capture);
34439 capture = parse211(capture);
34443 capture = parse231(capture);
34447 capture = parse294(capture);
34451 capture = parse370(capture);
34457 function linkErrorObject(d) {
34458 return "<a class=\"error_object_link\">".concat(d, "</a>");
34461 function linkEntity(d) {
34462 return "<a class=\"error_entity_link\">".concat(d, "</a>");
34465 function linkURL(d) {
34466 return "<a class=\"kr_external_link\" target=\"_blank\" href=\"".concat(d, "\">").concat(d, "</a>");
34467 } // arbitrary node list of form: #ID, #ID, #ID...
34470 function parse211(capture) {
34472 var items = capture.split(', ');
34473 items.forEach(function (item) {
34474 // ID has # at the front
34475 var id = linkEntity('n' + item.slice(1));
34478 return newList.join(', ');
34479 } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
34482 function parse231(capture) {
34483 var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),'
34485 var items = capture.split('),');
34486 items.forEach(function (item) {
34487 var match = item.match(/\#(\d+)\((.+)\)?/);
34489 if (match !== null && match.length > 2) {
34490 newList.push(linkEntity('w' + match[1]) + ' ' + _t('QA.keepRight.errorTypes.231.layer', {
34495 return newList.join(', ');
34496 } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
34499 function parse294(capture) {
34501 var items = capture.split(',');
34502 items.forEach(function (item) {
34503 // item of form "from/to node/relation #ID"
34504 item = item.split(' '); // to/from role is more clear in quotes
34506 var role = "\"".concat(item[0], "\""); // first letter of node/relation provides the type
34508 var idType = item[1].slice(0, 1); // ID has # at the front
34510 var id = item[2].slice(1);
34511 id = linkEntity(idType + id);
34512 newList.push("".concat(role, " ").concat(item[1], " ").concat(id));
34514 return newList.join(', ');
34515 } // may or may not include the string "(including the name 'name')"
34518 function parse370(capture) {
34519 if (!capture) return '';
34520 var match = capture.match(/\(including the name (\'.+\')\)/);
34522 if (match && match.length) {
34523 return _t('QA.keepRight.errorTypes.370.including_the_name', {
34529 } // arbitrary node list of form: #ID,#ID,#ID...
34532 function parse20(capture) {
34534 var items = capture.split(',');
34535 items.forEach(function (item) {
34536 // ID has # at the front
34537 var id = linkEntity('n' + item.slice(1));
34540 return newList.join(', ');
34544 var serviceKeepRight = {
34545 title: 'keepRight',
34546 init: function init() {
34547 _mainFileFetcher.get('keepRight').then(function (d) {
34548 return _krData = d;
34555 this.event = utilRebind(this, dispatch$1, 'on');
34557 reset: function reset() {
34559 Object.values(_cache.inflightTile).forEach(abortRequest);
34571 // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php
34572 loadIssues: function loadIssues(projection) {
34578 }; // determine the needed tiles to cover the view
34580 var tiles = tiler.zoomExtent([_tileZoom, _tileZoom]).getTiles(projection); // abort inflight requests that are no longer needed
34582 abortUnwantedRequests(_cache, tiles); // issue new requests..
34584 tiles.forEach(function (tile) {
34585 if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) return;
34587 var _tile$extent$rectangl = tile.extent.rectangle(),
34588 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
34589 left = _tile$extent$rectangl2[0],
34590 top = _tile$extent$rectangl2[1],
34591 right = _tile$extent$rectangl2[2],
34592 bottom = _tile$extent$rectangl2[3];
34594 var params = Object.assign({}, options, {
34600 var url = "".concat(_krUrlRoot, "/export.php?") + utilQsString(params);
34601 var controller = new AbortController();
34602 _cache.inflightTile[tile.id] = controller;
34604 signal: controller.signal
34605 }).then(function (data) {
34606 delete _cache.inflightTile[tile.id];
34607 _cache.loadedTile[tile.id] = true;
34609 if (!data || !data.features || !data.features.length) {
34610 throw new Error('No Data');
34613 data.features.forEach(function (feature) {
34614 var _feature$properties = feature.properties,
34615 itemType = _feature$properties.error_type,
34616 id = _feature$properties.error_id,
34617 _feature$properties$c = _feature$properties.comment,
34618 comment = _feature$properties$c === void 0 ? null : _feature$properties$c,
34619 objectId = _feature$properties.object_id,
34620 objectType = _feature$properties.object_type,
34621 schema = _feature$properties.schema,
34622 title = _feature$properties.title;
34623 var loc = feature.geometry.coordinates,
34624 _feature$properties$d = feature.properties.description,
34625 description = _feature$properties$d === void 0 ? '' : _feature$properties$d; // if there is a parent, save its error type e.g.:
34626 // Error 191 = "highway-highway"
34627 // Error 190 = "intersections without junctions" (parent)
34629 var issueTemplate = _krData.errorTypes[itemType];
34630 var parentIssueType = (Math.floor(itemType / 10) * 10).toString(); // try to handle error type directly, fallback to parent error type.
34632 var whichType = issueTemplate ? itemType : parentIssueType;
34633 var whichTemplate = _krData.errorTypes[whichType]; // Rewrite a few of the errors at this point..
34634 // This is done to make them easier to linkify and translate.
34636 switch (whichType) {
34638 description = "This feature has a FIXME tag: ".concat(description);
34643 description = description.replace('A turn-', 'This turn-');
34651 description = "This turn-restriction~".concat(description);
34655 description = 'This highway is missing a maxspeed tag';
34661 description = "This feature~".concat(description);
34663 } // move markers slightly so it doesn't obscure the geometry,
34664 // then move markers away from other coincident markers
34667 var coincident = false;
34670 // first time, move marker up. after that, move marker right.
34671 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
34672 loc = geoVecAdd(loc, delta);
34673 var bbox = geoExtent(loc).bbox();
34674 coincident = _cache.rtree.search(bbox).length;
34675 } while (coincident);
34677 var d = new QAItem(loc, _this, itemType, id, {
34679 description: description,
34680 whichType: whichType,
34681 parentIssueType: parentIssueType,
34682 severity: whichTemplate.severity || 'error',
34683 objectId: objectId,
34684 objectType: objectType,
34688 d.replacements = tokenReplacements(d);
34689 _cache.data[id] = d;
34691 _cache.rtree.insert(encodeIssueRtree(d));
34693 dispatch$1.call('loaded');
34694 })["catch"](function () {
34695 delete _cache.inflightTile[tile.id];
34696 _cache.loadedTile[tile.id] = true;
34700 postUpdate: function postUpdate(d, callback) {
34703 if (_cache.inflightPost[d.id]) {
34705 message: 'Error update already inflight',
34716 params.st = d.newStatus;
34719 if (d.newComment !== undefined) {
34720 params.co = d.newComment;
34721 } // NOTE: This throws a CORS err, but it seems successful.
34722 // We don't care too much about the response, so this is fine.
34725 var url = "".concat(_krUrlRoot, "/comment.php?") + utilQsString(params);
34726 var controller = new AbortController();
34727 _cache.inflightPost[d.id] = controller; // Since this is expected to throw an error just continue as if it worked
34728 // (worst case scenario the request truly fails and issue will show up if iD restarts)
34731 signal: controller.signal
34732 })["finally"](function () {
34733 delete _cache.inflightPost[d.id];
34735 if (d.newStatus === 'ignore') {
34736 // ignore permanently (false positive)
34737 _this2.removeItem(d);
34738 } else if (d.newStatus === 'ignore_t') {
34739 // ignore temporarily (error fixed)
34740 _this2.removeItem(d);
34742 _cache.closed["".concat(d.schema, ":").concat(d.id)] = true;
34744 d = _this2.replaceItem(d.update({
34745 comment: d.newComment,
34746 newComment: undefined,
34747 newState: undefined
34751 if (callback) callback(null, d);
34754 // Get all cached QAItems covering the viewport
34755 getItems: function getItems(projection) {
34756 var viewport = projection.clipExtent();
34757 var min = [viewport[0][0], viewport[1][1]];
34758 var max = [viewport[1][0], viewport[0][1]];
34759 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34760 return _cache.rtree.search(bbox).map(function (d) {
34764 // Get a QAItem from cache
34765 // NOTE: Don't change method name until UI v3 is merged
34766 getError: function getError(id) {
34767 return _cache.data[id];
34769 // Replace a single QAItem in the cache
34770 replaceItem: function replaceItem(item) {
34771 if (!(item instanceof QAItem) || !item.id) return;
34772 _cache.data[item.id] = item;
34773 updateRtree(encodeIssueRtree(item), true); // true = replace
34777 // Remove a single QAItem from the cache
34778 removeItem: function removeItem(item) {
34779 if (!(item instanceof QAItem) || !item.id) return;
34780 delete _cache.data[item.id];
34781 updateRtree(encodeIssueRtree(item), false); // false = remove
34783 issueURL: function issueURL(item) {
34784 return "".concat(_krUrlRoot, "/report_map.php?schema=").concat(item.schema, "&error=").concat(item.id);
34786 // Get an array of issues closed during this session.
34787 // Used to populate `closed:keepright` changeset tag
34788 getClosedIDs: function getClosedIDs() {
34789 return Object.keys(_cache.closed).sort();
34793 var tiler$1 = utilTiler();
34794 var dispatch$2 = dispatch('loaded');
34795 var _tileZoom$1 = 14;
34796 var _impOsmUrls = {
34797 ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
34798 mr: 'https://grab.community.improve-osm.org/missingGeoService',
34799 tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
34801 var _impOsmData = {
34803 }; // This gets reassigned if reset
34807 function abortRequest$1(i) {
34808 Object.values(i).forEach(function (controller) {
34810 controller.abort();
34815 function abortUnwantedRequests$1(cache, tiles) {
34816 Object.keys(cache.inflightTile).forEach(function (k) {
34817 var wanted = tiles.find(function (tile) {
34818 return k === tile.id;
34822 abortRequest$1(cache.inflightTile[k]);
34823 delete cache.inflightTile[k];
34828 function encodeIssueRtree$1(d) {
34836 } // Replace or remove QAItem from rtree
34839 function updateRtree$1(item, replace) {
34840 _cache$1.rtree.remove(item, function (a, b) {
34841 return a.data.id === b.data.id;
34845 _cache$1.rtree.insert(item);
34849 function linkErrorObject(d) {
34850 return "<a class=\"error_object_link\">".concat(d, "</a>");
34853 function linkEntity(d) {
34854 return "<a class=\"error_entity_link\">".concat(d, "</a>");
34857 function pointAverage(points) {
34858 if (points.length) {
34859 var sum = points.reduce(function (acc, point) {
34860 return geoVecAdd(acc, [point.lon, point.lat]);
34862 return geoVecScale(sum, 1 / points.length);
34868 function relativeBearing(p1, p2) {
34869 var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
34872 angle += 2 * Math.PI;
34873 } // Return degrees
34876 return angle * 180 / Math.PI;
34877 } // Assuming range [0,360)
34880 function cardinalDirection(bearing) {
34881 var dir = 45 * Math.round(bearing / 45);
34893 return _t("QA.improveOSM.directions.".concat(compass[dir]));
34894 } // Errors shouldn't obscure each other
34897 function preventCoincident(loc, bumpUp) {
34898 var coincident = false;
34901 // first time, move marker up. after that, move marker right.
34902 var delta = coincident ? [0.00001, 0] : bumpUp ? [0, 0.00001] : [0, 0];
34903 loc = geoVecAdd(loc, delta);
34904 var bbox = geoExtent(loc).bbox();
34905 coincident = _cache$1.rtree.search(bbox).length;
34906 } while (coincident);
34911 var serviceImproveOSM = {
34912 title: 'improveOSM',
34913 init: function init() {
34914 _mainFileFetcher.get('qa_data').then(function (d) {
34915 return _impOsmData = d.improveOSM;
34922 this.event = utilRebind(this, dispatch$2, 'on');
34924 reset: function reset() {
34926 Object.values(_cache$1.inflightTile).forEach(abortRequest$1);
34938 loadIssues: function loadIssues(projection) {
34944 zoom: '19' // Use a high zoom so that clusters aren't returned
34946 }; // determine the needed tiles to cover the view
34948 var tiles = tiler$1.zoomExtent([_tileZoom$1, _tileZoom$1]).getTiles(projection); // abort inflight requests that are no longer needed
34950 abortUnwantedRequests$1(_cache$1, tiles); // issue new requests..
34952 tiles.forEach(function (tile) {
34953 if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) return;
34955 var _tile$extent$rectangl = tile.extent.rectangle(),
34956 _tile$extent$rectangl2 = _slicedToArray(_tile$extent$rectangl, 4),
34957 east = _tile$extent$rectangl2[0],
34958 north = _tile$extent$rectangl2[1],
34959 west = _tile$extent$rectangl2[2],
34960 south = _tile$extent$rectangl2[3];
34962 var params = Object.assign({}, options, {
34967 }); // 3 separate requests to store for each tile
34970 Object.keys(_impOsmUrls).forEach(function (k) {
34971 // We exclude WATER from missing geometry as it doesn't seem useful
34972 // We use most confident one-way and turn restrictions only, still have false positives
34973 var kParams = Object.assign({}, params, k === 'mr' ? {
34974 type: 'PARKING,ROAD,BOTH,PATH'
34976 confidenceLevel: 'C1'
34978 var url = "".concat(_impOsmUrls[k], "/search?") + utilQsString(kParams);
34979 var controller = new AbortController();
34980 requests[k] = controller;
34982 signal: controller.signal
34983 }).then(function (data) {
34984 delete _cache$1.inflightTile[tile.id][k];
34986 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
34987 delete _cache$1.inflightTile[tile.id];
34988 _cache$1.loadedTile[tile.id] = true;
34989 } // Road segments at high zoom == oneways
34992 if (data.roadSegments) {
34993 data.roadSegments.forEach(function (feature) {
34994 // Position error at the approximate middle of the segment
34995 var points = feature.points,
34996 wayId = feature.wayId,
34997 fromNodeId = feature.fromNodeId,
34998 toNodeId = feature.toNodeId;
34999 var itemId = "".concat(wayId).concat(fromNodeId).concat(toNodeId);
35000 var mid = points.length / 2;
35001 var loc; // Even number of points, find midpoint of the middle two
35002 // Odd number of points, use position of very middle point
35004 if (mid % 1 === 0) {
35005 loc = pointAverage([points[mid - 1], points[mid]]);
35007 mid = points[Math.floor(mid)];
35008 loc = [mid.lon, mid.lat];
35009 } // One-ways can land on same segment in opposite direction
35012 loc = preventCoincident(loc, false);
35013 var d = new QAItem(loc, _this, k, itemId, {
35015 // used as a category
35017 // used to post changes
35019 fromNodeId: fromNodeId,
35024 }); // Variables used in the description
35027 percentage: feature.percentOfTrips,
35028 num_trips: feature.numberOfTrips,
35029 highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
35030 from_node: linkEntity('n' + feature.fromNodeId),
35031 to_node: linkEntity('n' + feature.toNodeId)
35033 _cache$1.data[d.id] = d;
35035 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35037 } // Tiles at high zoom == missing roads
35041 data.tiles.forEach(function (feature) {
35042 var type = feature.type,
35045 numberOfTrips = feature.numberOfTrips;
35046 var geoType = type.toLowerCase();
35047 var itemId = "".concat(geoType).concat(x).concat(y).concat(numberOfTrips); // Average of recorded points should land on the missing geometry
35048 // Missing geometry could happen to land on another error
35050 var loc = pointAverage(feature.points);
35051 loc = preventCoincident(loc, false);
35052 var d = new QAItem(loc, _this, "".concat(k, "-").concat(geoType), itemId, {
35060 num_trips: numberOfTrips,
35061 geometry_type: _t("QA.improveOSM.geometry_types.".concat(geoType))
35062 }; // -1 trips indicates data came from a 3rd party
35064 if (numberOfTrips === -1) {
35065 d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
35068 _cache$1.data[d.id] = d;
35070 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35072 } // Entities at high zoom == turn restrictions
35075 if (data.entities) {
35076 data.entities.forEach(function (feature) {
35077 var point = feature.point,
35079 segments = feature.segments,
35080 numberOfPasses = feature.numberOfPasses,
35081 turnType = feature.turnType;
35082 var itemId = "".concat(id.replace(/[,:+#]/g, '_')); // Turn restrictions could be missing at same junction
35083 // We also want to bump the error up so node is accessible
35085 var loc = preventCoincident([point.lon, point.lat], true); // Elements are presented in a strange way
35087 var ids = id.split(',');
35088 var from_way = ids[0];
35089 var via_node = ids[3];
35090 var to_way = ids[2].split(':')[1];
35091 var d = new QAItem(loc, _this, k, itemId, {
35094 objectId: via_node,
35096 }); // Travel direction along from_way clarifies the turn restriction
35098 var _segments$0$points = _slicedToArray(segments[0].points, 2),
35099 p1 = _segments$0$points[0],
35100 p2 = _segments$0$points[1];
35102 var dir_of_travel = cardinalDirection(relativeBearing(p1, p2)); // Variables used in the description
35105 num_passed: numberOfPasses,
35106 num_trips: segments[0].numberOfTrips,
35107 turn_restriction: turnType.toLowerCase(),
35108 from_way: linkEntity('w' + from_way),
35109 to_way: linkEntity('w' + to_way),
35110 travel_direction: dir_of_travel,
35111 junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
35113 _cache$1.data[d.id] = d;
35115 _cache$1.rtree.insert(encodeIssueRtree$1(d));
35117 dispatch$2.call('loaded');
35120 })["catch"](function () {
35121 delete _cache$1.inflightTile[tile.id][k];
35123 if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
35124 delete _cache$1.inflightTile[tile.id];
35125 _cache$1.loadedTile[tile.id] = true;
35129 _cache$1.inflightTile[tile.id] = requests;
35132 getComments: function getComments(item) {
35135 // If comments already retrieved no need to do so again
35136 if (item.comments) {
35137 return Promise.resolve(item);
35140 var key = item.issueKey;
35143 if (key === 'ow') {
35144 qParams = item.identifier;
35145 } else if (key === 'mr') {
35146 qParams.tileX = item.identifier.x;
35147 qParams.tileY = item.identifier.y;
35148 } else if (key === 'tr') {
35149 qParams.targetId = item.identifier;
35152 var url = "".concat(_impOsmUrls[key], "/retrieveComments?") + utilQsString(qParams);
35154 var cacheComments = function cacheComments(data) {
35155 // Assign directly for immediate use afterwards
35156 // comments are served newest to oldest
35157 item.comments = data.comments ? data.comments.reverse() : [];
35159 _this2.replaceItem(item);
35162 return d3_json(url).then(cacheComments).then(function () {
35166 postUpdate: function postUpdate(d, callback) {
35167 if (!serviceOsm.authenticated()) {
35168 // Username required in payload
35170 message: 'Not Authenticated',
35175 if (_cache$1.inflightPost[d.id]) {
35177 message: 'Error update already inflight',
35180 } // Payload can only be sent once username is established
35183 serviceOsm.userDetails(sendPayload.bind(this));
35185 function sendPayload(err, user) {
35189 return callback(err, d);
35192 var key = d.issueKey;
35193 var url = "".concat(_impOsmUrls[key], "/comment");
35195 username: user.display_name,
35196 targetIds: [d.identifier]
35200 payload.status = d.newStatus;
35201 payload.text = 'status changed';
35202 } // Comment take place of default text
35205 if (d.newComment) {
35206 payload.text = d.newComment;
35209 var controller = new AbortController();
35210 _cache$1.inflightPost[d.id] = controller;
35213 signal: controller.signal,
35214 body: JSON.stringify(payload)
35216 d3_json(url, options).then(function () {
35217 delete _cache$1.inflightPost[d.id]; // Just a comment, update error in cache
35219 if (!d.newStatus) {
35220 var now = new Date();
35221 var comments = d.comments ? d.comments : [];
35223 username: payload.username,
35224 text: payload.text,
35225 timestamp: now.getTime() / 1000
35228 _this3.replaceItem(d.update({
35229 comments: comments,
35230 newComment: undefined
35233 _this3.removeItem(d);
35235 if (d.newStatus === 'SOLVED') {
35236 // Keep track of the number of issues closed per type to tag the changeset
35237 if (!(d.issueKey in _cache$1.closed)) {
35238 _cache$1.closed[d.issueKey] = 0;
35241 _cache$1.closed[d.issueKey] += 1;
35245 if (callback) callback(null, d);
35246 })["catch"](function (err) {
35247 delete _cache$1.inflightPost[d.id];
35248 if (callback) callback(err.message);
35252 // Get all cached QAItems covering the viewport
35253 getItems: function getItems(projection) {
35254 var viewport = projection.clipExtent();
35255 var min = [viewport[0][0], viewport[1][1]];
35256 var max = [viewport[1][0], viewport[0][1]];
35257 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
35258 return _cache$1.rtree.search(bbox).map(function (d) {
35262 // Get a QAItem from cache
35263 // NOTE: Don't change method name until UI v3 is merged
35264 getError: function getError(id) {
35265 return _cache$1.data[id];
35267 // get the name of the icon to display for this item
35268 getIcon: function getIcon(itemType) {
35269 return _impOsmData.icons[itemType];
35271 // Replace a single QAItem in the cache
35272 replaceItem: function replaceItem(issue) {
35273 if (!(issue instanceof QAItem) || !issue.id) return;
35274 _cache$1.data[issue.id] = issue;
35275 updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace
35279 // Remove a single QAItem from the cache
35280 removeItem: function removeItem(issue) {
35281 if (!(issue instanceof QAItem) || !issue.id) return;
35282 delete _cache$1.data[issue.id];
35283 updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove
35285 // Used to populate `closed:improveosm:*` changeset tags
35286 getClosedCounts: function getClosedCounts() {
35287 return _cache$1.closed;
35293 // B.2.3.2.1 CreateHTML(string, tag, attribute, value)
35294 // https://tc39.github.io/ecma262/#sec-createhtml
35295 var createHtml = function (string, tag, attribute, value) {
35296 var S = String(requireObjectCoercible(string));
35297 var p1 = '<' + tag;
35298 if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '"') + '"';
35299 return p1 + '>' + S + '</' + tag + '>';
35302 // check the existence of a method, lowercase
35303 // of a tag and escaping quotes in arguments
35304 var stringHtmlForced = function (METHOD_NAME) {
35305 return fails(function () {
35306 var test = ''[METHOD_NAME]('"');
35307 return test !== test.toLowerCase() || test.split('"').length > 3;
35311 // `String.prototype.link` method
35312 // https://tc39.github.io/ecma262/#sec-string.prototype.link
35313 _export({ target: 'String', proto: true, forced: stringHtmlForced('link') }, {
35314 link: function link(url) {
35315 return createHtml(this, 'a', 'href', url);
35319 var getOwnPropertyDescriptor$4 = objectGetOwnPropertyDescriptor.f;
35326 var nativeEndsWith = ''.endsWith;
35327 var min$8 = Math.min;
35329 var CORRECT_IS_REGEXP_LOGIC = correctIsRegexpLogic('endsWith');
35330 // https://github.com/zloirock/core-js/pull/702
35331 var MDN_POLYFILL_BUG = !CORRECT_IS_REGEXP_LOGIC && !!function () {
35332 var descriptor = getOwnPropertyDescriptor$4(String.prototype, 'endsWith');
35333 return descriptor && !descriptor.writable;
35336 // `String.prototype.endsWith` method
35337 // https://tc39.github.io/ecma262/#sec-string.prototype.endswith
35338 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {
35339 endsWith: function endsWith(searchString /* , endPosition = @length */) {
35340 var that = String(requireObjectCoercible(this));
35341 notARegexp(searchString);
35342 var endPosition = arguments.length > 1 ? arguments[1] : undefined;
35343 var len = toLength(that.length);
35344 var end = endPosition === undefined ? len : min$8(toLength(endPosition), len);
35345 var search = String(searchString);
35346 return nativeEndsWith
35347 ? nativeEndsWith.call(that, search, end)
35348 : that.slice(end - search.length, end) === search;
35352 var getOwnPropertyDescriptor$5 = objectGetOwnPropertyDescriptor.f;
35359 var nativeStartsWith = ''.startsWith;
35360 var min$9 = Math.min;
35362 var CORRECT_IS_REGEXP_LOGIC$1 = correctIsRegexpLogic('startsWith');
35363 // https://github.com/zloirock/core-js/pull/702
35364 var MDN_POLYFILL_BUG$1 = !CORRECT_IS_REGEXP_LOGIC$1 && !!function () {
35365 var descriptor = getOwnPropertyDescriptor$5(String.prototype, 'startsWith');
35366 return descriptor && !descriptor.writable;
35369 // `String.prototype.startsWith` method
35370 // https://tc39.github.io/ecma262/#sec-string.prototype.startswith
35371 _export({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG$1 && !CORRECT_IS_REGEXP_LOGIC$1 }, {
35372 startsWith: function startsWith(searchString /* , position = 0 */) {
35373 var that = String(requireObjectCoercible(this));
35374 notARegexp(searchString);
35375 var index = toLength(min$9(arguments.length > 1 ? arguments[1] : undefined, that.length));
35376 var search = String(searchString);
35377 return nativeStartsWith
35378 ? nativeStartsWith.call(that, search, index)
35379 : that.slice(index, index + search.length) === search;
35383 var $trimEnd = stringTrim.end;
35386 var FORCED$e = stringTrimForced('trimEnd');
35388 var trimEnd = FORCED$e ? function trimEnd() {
35389 return $trimEnd(this);
35392 // `String.prototype.{ trimEnd, trimRight }` methods
35393 // https://github.com/tc39/ecmascript-string-left-right-trim
35394 _export({ target: 'String', proto: true, forced: FORCED$e }, {
35399 var defaults = createCommonjsModule(function (module) {
35400 function getDefaults() {
35408 langPrefix: 'language-',
35416 smartypants: false,
35423 function changeDefaults(newDefaults) {
35424 module.exports.defaults = newDefaults;
35428 defaults: getDefaults(),
35429 getDefaults: getDefaults,
35430 changeDefaults: changeDefaults
35437 var escapeTest = /[&<>"']/;
35438 var escapeReplace = /[&<>"']/g;
35439 var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
35440 var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
35441 var escapeReplacements = {
35449 var getEscapeReplacement = function getEscapeReplacement(ch) {
35450 return escapeReplacements[ch];
35453 function escape$1(html, encode) {
35455 if (escapeTest.test(html)) {
35456 return html.replace(escapeReplace, getEscapeReplacement);
35459 if (escapeTestNoEncode.test(html)) {
35460 return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
35467 var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
35469 function unescape$1(html) {
35470 // explicitly match decimal, hex, and named HTML entities
35471 return html.replace(unescapeTest, function (_, n) {
35472 n = n.toLowerCase();
35473 if (n === 'colon') return ':';
35475 if (n.charAt(0) === '#') {
35476 return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1));
35483 var caret = /(^|[^\[])\^/g;
35485 function edit(regex, opt) {
35486 regex = regex.source || regex;
35489 replace: function replace(name, val) {
35490 val = val.source || val;
35491 val = val.replace(caret, '$1');
35492 regex = regex.replace(name, val);
35495 getRegex: function getRegex() {
35496 return new RegExp(regex, opt);
35502 var nonWordAndColonTest = /[^\w:]/g;
35503 var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
35505 function cleanUrl(sanitize, base, href) {
35510 prot = decodeURIComponent(unescape$1(href)).replace(nonWordAndColonTest, '').toLowerCase();
35515 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
35520 if (base && !originIndependentUrl.test(href)) {
35521 href = resolveUrl(base, href);
35525 href = encodeURI(href).replace(/%25/g, '%');
35534 var justDomain = /^[^:]+:\/*[^/]*$/;
35535 var protocol = /^([^:]+:)[\s\S]*$/;
35536 var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
35538 function resolveUrl(base, href) {
35539 if (!baseUrls[' ' + base]) {
35540 // we can ignore everything in base after the last slash of its path component,
35541 // but we might need to add _that_
35542 // https://tools.ietf.org/html/rfc3986#section-3
35543 if (justDomain.test(base)) {
35544 baseUrls[' ' + base] = base + '/';
35546 baseUrls[' ' + base] = rtrim$1(base, '/', true);
35550 base = baseUrls[' ' + base];
35551 var relativeBase = base.indexOf(':') === -1;
35553 if (href.substring(0, 2) === '//') {
35554 if (relativeBase) {
35558 return base.replace(protocol, '$1') + href;
35559 } else if (href.charAt(0) === '/') {
35560 if (relativeBase) {
35564 return base.replace(domain, '$1') + href;
35566 return base + href;
35571 exec: function noopTest() {}
35574 function merge$1(obj) {
35579 for (; i < arguments.length; i++) {
35580 target = arguments[i];
35582 for (key in target) {
35583 if (Object.prototype.hasOwnProperty.call(target, key)) {
35584 obj[key] = target[key];
35592 function splitCells(tableRow, count) {
35593 // ensure that every cell-delimiting pipe has a space
35594 // before it to distinguish it from an escaped pipe
35595 var row = tableRow.replace(/\|/g, function (match, offset, str) {
35596 var escaped = false,
35599 while (--curr >= 0 && str[curr] === '\\') {
35600 escaped = !escaped;
35604 // odd number of slashes means | is escaped
35605 // so we leave it alone
35608 // add space before unescaped |
35612 cells = row.split(/ \|/);
35615 if (cells.length > count) {
35616 cells.splice(count);
35618 while (cells.length < count) {
35623 for (; i < cells.length; i++) {
35624 // leading or trailing whitespace is ignored per the gfm spec
35625 cells[i] = cells[i].trim().replace(/\\\|/g, '|');
35629 } // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
35630 // /c*$/ is vulnerable to REDOS.
35631 // invert: Remove suffix of non-c chars instead. Default falsey.
35634 function rtrim$1(str, c, invert) {
35635 var l = str.length;
35639 } // Length of suffix matching the invert condition.
35642 var suffLen = 0; // Step left until we fail to match the invert condition.
35644 while (suffLen < l) {
35645 var currChar = str.charAt(l - suffLen - 1);
35647 if (currChar === c && !invert) {
35649 } else if (currChar !== c && invert) {
35656 return str.substr(0, l - suffLen);
35659 function findClosingBracket(str, b) {
35660 if (str.indexOf(b[1]) === -1) {
35664 var l = str.length;
35668 for (; i < l; i++) {
35669 if (str[i] === '\\') {
35671 } else if (str[i] === b[0]) {
35673 } else if (str[i] === b[1]) {
35685 function checkSanitizeDeprecation(opt) {
35686 if (opt && opt.sanitize && !opt.silent) {
35687 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');
35689 } // copied from https://stackoverflow.com/a/5450113/806777
35692 function repeatString(pattern, count) {
35699 while (count > 1) {
35705 pattern += pattern;
35708 return result + pattern;
35713 unescape: unescape$1,
35715 cleanUrl: cleanUrl,
35716 resolveUrl: resolveUrl,
35717 noopTest: noopTest,
35719 splitCells: splitCells,
35721 findClosingBracket: findClosingBracket,
35722 checkSanitizeDeprecation: checkSanitizeDeprecation,
35723 repeatString: repeatString
35726 var defaults$1 = defaults.defaults;
35727 var rtrim$2 = helpers.rtrim,
35728 splitCells$1 = helpers.splitCells,
35729 _escape = helpers.escape,
35730 findClosingBracket$1 = helpers.findClosingBracket;
35732 function outputLink(cap, link, raw) {
35733 var href = link.href;
35734 var title = link.title ? _escape(link.title) : null;
35735 var text = cap[1].replace(/\\([\[\]])/g, '$1');
35737 if (cap[0].charAt(0) !== '!') {
35751 text: _escape(text)
35756 function indentCodeCompensation(raw, text) {
35757 var matchIndentToCode = raw.match(/^(\s+)(?:```)/);
35759 if (matchIndentToCode === null) {
35763 var indentToCode = matchIndentToCode[1];
35764 return text.split('\n').map(function (node) {
35765 var matchIndentInNode = node.match(/^\s+/);
35767 if (matchIndentInNode === null) {
35771 var _matchIndentInNode = _slicedToArray(matchIndentInNode, 1),
35772 indentInNode = _matchIndentInNode[0];
35774 if (indentInNode.length >= indentToCode.length) {
35775 return node.slice(indentToCode.length);
35786 var Tokenizer_1 = /*#__PURE__*/function () {
35787 function Tokenizer(options) {
35788 _classCallCheck(this, Tokenizer);
35790 this.options = options || defaults$1;
35793 _createClass(Tokenizer, [{
35795 value: function space(src) {
35796 var cap = this.rules.block.newline.exec(src);
35799 if (cap[0].length > 1) {
35813 value: function code(src, tokens) {
35814 var cap = this.rules.block.code.exec(src);
35817 var lastToken = tokens[tokens.length - 1]; // An indented code block cannot interrupt a paragraph.
35819 if (lastToken && lastToken.type === 'paragraph') {
35822 text: cap[0].trimRight()
35826 var text = cap[0].replace(/^ {4}/gm, '');
35830 codeBlockStyle: 'indented',
35831 text: !this.options.pedantic ? rtrim$2(text, '\n') : text
35837 value: function fences(src) {
35838 var cap = this.rules.block.fences.exec(src);
35842 var text = indentCodeCompensation(raw, cap[3] || '');
35846 lang: cap[2] ? cap[2].trim() : cap[2],
35853 value: function heading(src) {
35854 var cap = this.rules.block.heading.exec(src);
35860 depth: cap[1].length,
35867 value: function nptable(src) {
35868 var cap = this.rules.block.nptable.exec(src);
35873 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
35874 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
35875 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
35879 if (item.header.length === item.align.length) {
35880 var l = item.align.length;
35883 for (i = 0; i < l; i++) {
35884 if (/^ *-+: *$/.test(item.align[i])) {
35885 item.align[i] = 'right';
35886 } else if (/^ *:-+: *$/.test(item.align[i])) {
35887 item.align[i] = 'center';
35888 } else if (/^ *:-+ *$/.test(item.align[i])) {
35889 item.align[i] = 'left';
35891 item.align[i] = null;
35895 l = item.cells.length;
35897 for (i = 0; i < l; i++) {
35898 item.cells[i] = splitCells$1(item.cells[i], item.header.length);
35907 value: function hr(src) {
35908 var cap = this.rules.block.hr.exec(src);
35919 value: function blockquote(src) {
35920 var cap = this.rules.block.blockquote.exec(src);
35923 var text = cap[0].replace(/^ *> ?/gm, '');
35925 type: 'blockquote',
35933 value: function list(src) {
35934 var cap = this.rules.block.list.exec(src);
35939 var isordered = bull.length > 1;
35940 var isparen = bull[bull.length - 1] === ')';
35944 ordered: isordered,
35945 start: isordered ? +bull.slice(0, -1) : '',
35948 }; // Get each top-level item.
35950 var itemMatch = cap[0].match(this.rules.block.item);
35959 var l = itemMatch.length;
35961 for (var i = 0; i < l; i++) {
35962 item = itemMatch[i];
35963 raw = item; // Remove the list item's bullet
35964 // so it is seen as the next token.
35966 space = item.length;
35967 item = item.replace(/^ *([*+-]|\d+[.)]) ?/, ''); // Outdent whatever the
35968 // list item contains. Hacky.
35970 if (~item.indexOf('\n ')) {
35971 space -= item.length;
35972 item = !this.options.pedantic ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') : item.replace(/^ {1,4}/gm, '');
35973 } // Determine whether the next list item belongs here.
35974 // Backpedal if it does not belong in this list.
35978 b = this.rules.block.bullet.exec(itemMatch[i + 1])[0];
35980 if (isordered ? b.length === 1 || !isparen && b[b.length - 1] === ')' : b.length > 1 || this.options.smartLists && b !== bull) {
35981 addBack = itemMatch.slice(i + 1).join('\n');
35982 list.raw = list.raw.substring(0, list.raw.length - addBack.length);
35985 } // Determine whether item is loose or not.
35986 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
35987 // for discount behavior.
35990 loose = next || /\n\n(?!\s*$)/.test(item);
35993 next = item.charAt(item.length - 1) === '\n';
35994 if (!loose) loose = next;
35999 } // Check for task list items
36002 istask = /^\[[ xX]\] /.test(item);
36003 ischecked = undefined;
36006 ischecked = item[1] !== ' ';
36007 item = item.replace(/^\[[ xX]\] +/, '');
36014 checked: ischecked,
36025 value: function html(src) {
36026 var cap = this.rules.block.html.exec(src);
36030 type: this.options.sanitize ? 'paragraph' : 'html',
36032 pre: !this.options.sanitizer && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
36033 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
36039 value: function def(src) {
36040 var cap = this.rules.block.def.exec(src);
36043 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
36044 var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
36055 value: function table(src) {
36056 var cap = this.rules.block.table.exec(src);
36061 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
36062 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
36063 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
36066 if (item.header.length === item.align.length) {
36068 var l = item.align.length;
36071 for (i = 0; i < l; i++) {
36072 if (/^ *-+: *$/.test(item.align[i])) {
36073 item.align[i] = 'right';
36074 } else if (/^ *:-+: *$/.test(item.align[i])) {
36075 item.align[i] = 'center';
36076 } else if (/^ *:-+ *$/.test(item.align[i])) {
36077 item.align[i] = 'left';
36079 item.align[i] = null;
36083 l = item.cells.length;
36085 for (i = 0; i < l; i++) {
36086 item.cells[i] = splitCells$1(item.cells[i].replace(/^ *\| *| *\| *$/g, ''), item.header.length);
36095 value: function lheading(src) {
36096 var cap = this.rules.block.lheading.exec(src);
36102 depth: cap[2].charAt(0) === '=' ? 1 : 2,
36109 value: function paragraph(src) {
36110 var cap = this.rules.block.paragraph.exec(src);
36116 text: cap[1].charAt(cap[1].length - 1) === '\n' ? cap[1].slice(0, -1) : cap[1]
36122 value: function text(src, tokens) {
36123 var cap = this.rules.block.text.exec(src);
36126 var lastToken = tokens[tokens.length - 1];
36128 if (lastToken && lastToken.type === 'text') {
36144 value: function escape(src) {
36145 var cap = this.rules.inline.escape.exec(src);
36151 text: _escape(cap[1])
36157 value: function tag(src, inLink, inRawBlock) {
36158 var cap = this.rules.inline.tag.exec(src);
36161 if (!inLink && /^<a /i.test(cap[0])) {
36163 } else if (inLink && /^<\/a>/i.test(cap[0])) {
36167 if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
36169 } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
36170 inRawBlock = false;
36174 type: this.options.sanitize ? 'text' : 'html',
36177 inRawBlock: inRawBlock,
36178 text: this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0]
36184 value: function link(src) {
36185 var cap = this.rules.inline.link.exec(src);
36188 var lastParenIndex = findClosingBracket$1(cap[2], '()');
36190 if (lastParenIndex > -1) {
36191 var start = cap[0].indexOf('!') === 0 ? 5 : 4;
36192 var linkLen = start + cap[1].length + lastParenIndex;
36193 cap[2] = cap[2].substring(0, lastParenIndex);
36194 cap[0] = cap[0].substring(0, linkLen).trim();
36201 if (this.options.pedantic) {
36202 var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
36211 title = cap[3] ? cap[3].slice(1, -1) : '';
36214 href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
36215 var token = outputLink(cap, {
36216 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
36217 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
36224 value: function reflink(src, links) {
36227 if ((cap = this.rules.inline.reflink.exec(src)) || (cap = this.rules.inline.nolink.exec(src))) {
36228 var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
36229 link = links[link.toLowerCase()];
36231 if (!link || !link.href) {
36232 var text = cap[0].charAt(0);
36240 var token = outputLink(cap, link, cap[0]);
36246 value: function strong(src, maskedSrc) {
36247 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
36248 var match = this.rules.inline.strong.start.exec(src);
36250 if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
36251 maskedSrc = maskedSrc.slice(-1 * src.length);
36252 var endReg = match[0] === '**' ? this.rules.inline.strong.endAst : this.rules.inline.strong.endUnd;
36253 endReg.lastIndex = 0;
36256 while ((match = endReg.exec(maskedSrc)) != null) {
36257 cap = this.rules.inline.strong.middle.exec(maskedSrc.slice(0, match.index + 3));
36262 raw: src.slice(0, cap[0].length),
36263 text: src.slice(2, cap[0].length - 2)
36271 value: function em(src, maskedSrc) {
36272 var prevChar = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
36273 var match = this.rules.inline.em.start.exec(src);
36275 if (match && (!match[1] || match[1] && (prevChar === '' || this.rules.inline.punctuation.exec(prevChar)))) {
36276 maskedSrc = maskedSrc.slice(-1 * src.length);
36277 var endReg = match[0] === '*' ? this.rules.inline.em.endAst : this.rules.inline.em.endUnd;
36278 endReg.lastIndex = 0;
36281 while ((match = endReg.exec(maskedSrc)) != null) {
36282 cap = this.rules.inline.em.middle.exec(maskedSrc.slice(0, match.index + 2));
36287 raw: src.slice(0, cap[0].length),
36288 text: src.slice(1, cap[0].length - 1)
36296 value: function codespan(src) {
36297 var cap = this.rules.inline.code.exec(src);
36300 var text = cap[2].replace(/\n/g, ' ');
36301 var hasNonSpaceChars = /[^ ]/.test(text);
36302 var hasSpaceCharsOnBothEnds = text.startsWith(' ') && text.endsWith(' ');
36304 if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
36305 text = text.substring(1, text.length - 1);
36308 text = _escape(text, true);
36318 value: function br(src) {
36319 var cap = this.rules.inline.br.exec(src);
36330 value: function del(src) {
36331 var cap = this.rules.inline.del.exec(src);
36343 value: function autolink(src, mangle) {
36344 var cap = this.rules.inline.autolink.exec(src);
36349 if (cap[2] === '@') {
36350 text = _escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
36351 href = 'mailto:' + text;
36353 text = _escape(cap[1]);
36372 value: function url(src, mangle) {
36375 if (cap = this.rules.inline.url.exec(src)) {
36378 if (cap[2] === '@') {
36379 text = _escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
36380 href = 'mailto:' + text;
36382 // do extended autolink path validation
36386 prevCapZero = cap[0];
36387 cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
36388 } while (prevCapZero !== cap[0]);
36390 text = _escape(cap[0]);
36392 if (cap[1] === 'www.') {
36393 href = 'http://' + text;
36414 value: function inlineText(src, inRawBlock, smartypants) {
36415 var cap = this.rules.inline.text.exec(src);
36421 text = this.options.sanitize ? this.options.sanitizer ? this.options.sanitizer(cap[0]) : _escape(cap[0]) : cap[0];
36423 text = _escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
36438 var noopTest$1 = helpers.noopTest,
36439 edit$1 = helpers.edit,
36440 merge$2 = helpers.merge;
36442 * Block-Level Grammar
36447 code: /^( {4}[^\n]+\n*)+/,
36448 fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
36449 hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
36450 heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
36451 blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
36452 list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
36453 html: '^ {0,3}(?:' // optional indentation
36454 + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
36455 + '|comment[^\\n]*(\\n+|$)' // (2)
36456 + '|<\\?[\\s\\S]*?(?:\\?>\\n*|$)' // (3)
36457 + '|<![A-Z][\\s\\S]*?(?:>\\n*|$)' // (4)
36458 + '|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)' // (5)
36459 + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
36460 + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
36461 + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
36463 def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
36464 nptable: noopTest$1,
36466 lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
36467 // regex template, placeholders will be replaced according to different paragraph
36468 // interruption rules of commonmark and the original markdown spec:
36469 _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
36472 block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
36473 block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
36474 block.def = edit$1(block.def).replace('label', block._label).replace('title', block._title).getRegex();
36475 block.bullet = /(?:[*+-]|\d{1,9}[.)])/;
36476 block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
36477 block.item = edit$1(block.item, 'gm').replace(/bull/g, block.bullet).getRegex();
36478 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();
36479 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';
36480 block._comment = /<!--(?!-?>)[\s\S]*?(?:-->|$)/;
36481 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();
36482 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
36483 .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
36484 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
36486 block.blockquote = edit$1(block.blockquote).replace('paragraph', block.paragraph).getRegex();
36488 * Normal Block Grammar
36491 block.normal = merge$2({}, block);
36493 * GFM Block Grammar
36496 block.gfm = merge$2({}, block.normal, {
36497 nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
36498 + ' {0,3}([-:]+ *\\|[-| :]*)' // Align
36499 + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)',
36501 table: '^ *\\|(.+)\\n' // Header
36502 + ' {0,3}\\|?( *[-:]+[-| :]*)' // Align
36503 + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
36506 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
36507 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
36509 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
36510 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)').replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
36513 * Pedantic grammar (original John Gruber's loose markdown specification)
36516 block.pedantic = merge$2({}, block.normal, {
36517 html: edit$1('^ *(?:comment *(?:\\n|\\s*$)' + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
36518 + '|<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(),
36519 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
36520 heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
36521 fences: noopTest$1,
36522 // fences not supported
36523 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()
36526 * Inline-Level Grammar
36530 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
36531 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
36533 tag: '^comment' + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
36534 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
36535 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
36536 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
36537 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
36539 link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
36540 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
36541 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
36542 reflinkSearch: 'reflink|nolink(?!\\()',
36544 start: /^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,
36545 // (1) returns if starts w/ punctuation
36546 middle: /^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,
36547 endAst: /[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,
36548 // last char can't be punct, or final * must also be followed by punct (or endline)
36549 endUnd: /[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)
36553 start: /^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,
36554 // (1) returns if starts w/ punctuation
36555 middle: /^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,
36556 endAst: /[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,
36557 // last char can't be punct, or final * must also be followed by punct (or endline)
36558 endUnd: /[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/ // last char can't be a space, and final _ must preceed punct or \s (or endline)
36561 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
36562 br: /^( {2,}|\\)\n(?!\s*$)/,
36564 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n)))/,
36565 punctuation: /^([\s*punctuation])/
36566 }; // list of punctuation marks from common mark spec
36567 // without * and _ to workaround cases with double emphasis
36569 inline._punctuation = '!"#$%&\'()+\\-.,/:;<=>?@\\[\\]`^{|}~';
36570 inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, <html>
36572 inline._blockSkip = '\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>';
36573 inline._overlapSkip = '__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*';
36574 inline._comment = edit$1(block._comment).replace('(?:-->|$)', '-->').getRegex();
36575 inline.em.start = edit$1(inline.em.start).replace(/punctuation/g, inline._punctuation).getRegex();
36576 inline.em.middle = edit$1(inline.em.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
36577 inline.em.endAst = edit$1(inline.em.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36578 inline.em.endUnd = edit$1(inline.em.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36579 inline.strong.start = edit$1(inline.strong.start).replace(/punctuation/g, inline._punctuation).getRegex();
36580 inline.strong.middle = edit$1(inline.strong.middle).replace(/punctuation/g, inline._punctuation).replace(/overlapSkip/g, inline._overlapSkip).getRegex();
36581 inline.strong.endAst = edit$1(inline.strong.endAst, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36582 inline.strong.endUnd = edit$1(inline.strong.endUnd, 'g').replace(/punctuation/g, inline._punctuation).getRegex();
36583 inline.blockSkip = edit$1(inline._blockSkip, 'g').getRegex();
36584 inline.overlapSkip = edit$1(inline._overlapSkip, 'g').getRegex();
36585 inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
36586 inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
36587 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])?)+(?![-_])/;
36588 inline.autolink = edit$1(inline.autolink).replace('scheme', inline._scheme).replace('email', inline._email).getRegex();
36589 inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
36590 inline.tag = edit$1(inline.tag).replace('comment', inline._comment).replace('attribute', inline._attribute).getRegex();
36591 inline._label = /(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
36592 inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
36593 inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
36594 inline.link = edit$1(inline.link).replace('label', inline._label).replace('href', inline._href).replace('title', inline._title).getRegex();
36595 inline.reflink = edit$1(inline.reflink).replace('label', inline._label).getRegex();
36596 inline.reflinkSearch = edit$1(inline.reflinkSearch, 'g').replace('reflink', inline.reflink).replace('nolink', inline.nolink).getRegex();
36598 * Normal Inline Grammar
36601 inline.normal = merge$2({}, inline);
36603 * Pedantic Inline Grammar
36606 inline.pedantic = merge$2({}, inline.normal, {
36609 middle: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
36610 endAst: /\*\*(?!\*)/g,
36615 middle: /^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
36616 endAst: /\*(?!\*)/g,
36619 link: edit$1(/^!?\[(label)\]\((.*?)\)/).replace('label', inline._label).getRegex(),
36620 reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace('label', inline._label).getRegex()
36623 * GFM Inline Grammar
36626 inline.gfm = merge$2({}, inline.normal, {
36627 escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
36628 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
36629 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
36630 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
36631 del: /^~+(?=\S)([\s\S]*?\S)~+/,
36632 text: /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
36634 inline.gfm.url = edit$1(inline.gfm.url, 'i').replace('email', inline.gfm._extended_email).getRegex();
36636 * GFM + Line Breaks Inline Grammar
36639 inline.breaks = merge$2({}, inline.gfm, {
36640 br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
36641 text: edit$1(inline.gfm.text).replace('\\b_', '\\b_| {2,}\\n').replace(/\{2,\}/g, '*').getRegex()
36648 var defaults$2 = defaults.defaults;
36649 var block$1 = rules.block,
36650 inline$1 = rules.inline;
36651 var repeatString$1 = helpers.repeatString;
36653 * smartypants text replacement
36656 function smartypants(text) {
36657 return text // em-dashes
36658 .replace(/---/g, "\u2014") // en-dashes
36659 .replace(/--/g, "\u2013") // opening singles
36660 .replace(/(^|[-\u2014/(\[{"\s])'/g, "$1\u2018") // closing singles & apostrophes
36661 .replace(/'/g, "\u2019") // opening doubles
36662 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, "$1\u201C") // closing doubles
36663 .replace(/"/g, "\u201D") // ellipses
36664 .replace(/\.{3}/g, "\u2026");
36667 * mangle email addresses
36671 function mangle(text) {
36675 var l = text.length;
36677 for (i = 0; i < l; i++) {
36678 ch = text.charCodeAt(i);
36680 if (Math.random() > 0.5) {
36681 ch = 'x' + ch.toString(16);
36684 out += '&#' + ch + ';';
36694 var Lexer_1 = /*#__PURE__*/function () {
36695 function Lexer(options) {
36696 _classCallCheck(this, Lexer);
36699 this.tokens.links = Object.create(null);
36700 this.options = options || defaults$2;
36701 this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
36702 this.tokenizer = this.options.tokenizer;
36703 this.tokenizer.options = this.options;
36705 block: block$1.normal,
36706 inline: inline$1.normal
36709 if (this.options.pedantic) {
36710 rules.block = block$1.pedantic;
36711 rules.inline = inline$1.pedantic;
36712 } else if (this.options.gfm) {
36713 rules.block = block$1.gfm;
36715 if (this.options.breaks) {
36716 rules.inline = inline$1.breaks;
36718 rules.inline = inline$1.gfm;
36722 this.tokenizer.rules = rules;
36729 _createClass(Lexer, [{
36735 value: function lex(src) {
36736 src = src.replace(/\r\n|\r/g, '\n').replace(/\t/g, ' ');
36737 this.blockTokens(src, this.tokens, true);
36738 this.inline(this.tokens);
36739 return this.tokens;
36746 key: "blockTokens",
36747 value: function blockTokens(src) {
36748 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
36749 var top = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
36750 src = src.replace(/^ +$/gm, '');
36751 var token, i, l, lastToken;
36755 if (token = this.tokenizer.space(src)) {
36756 src = src.substring(token.raw.length);
36759 tokens.push(token);
36766 if (token = this.tokenizer.code(src, tokens)) {
36767 src = src.substring(token.raw.length);
36770 tokens.push(token);
36772 lastToken = tokens[tokens.length - 1];
36773 lastToken.raw += '\n' + token.raw;
36774 lastToken.text += '\n' + token.text;
36781 if (token = this.tokenizer.fences(src)) {
36782 src = src.substring(token.raw.length);
36783 tokens.push(token);
36788 if (token = this.tokenizer.heading(src)) {
36789 src = src.substring(token.raw.length);
36790 tokens.push(token);
36792 } // table no leading pipe (gfm)
36795 if (token = this.tokenizer.nptable(src)) {
36796 src = src.substring(token.raw.length);
36797 tokens.push(token);
36802 if (token = this.tokenizer.hr(src)) {
36803 src = src.substring(token.raw.length);
36804 tokens.push(token);
36809 if (token = this.tokenizer.blockquote(src)) {
36810 src = src.substring(token.raw.length);
36811 token.tokens = this.blockTokens(token.text, [], top);
36812 tokens.push(token);
36817 if (token = this.tokenizer.list(src)) {
36818 src = src.substring(token.raw.length);
36819 l = token.items.length;
36821 for (i = 0; i < l; i++) {
36822 token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
36825 tokens.push(token);
36830 if (token = this.tokenizer.html(src)) {
36831 src = src.substring(token.raw.length);
36832 tokens.push(token);
36837 if (top && (token = this.tokenizer.def(src))) {
36838 src = src.substring(token.raw.length);
36840 if (!this.tokens.links[token.tag]) {
36841 this.tokens.links[token.tag] = {
36851 if (token = this.tokenizer.table(src)) {
36852 src = src.substring(token.raw.length);
36853 tokens.push(token);
36858 if (token = this.tokenizer.lheading(src)) {
36859 src = src.substring(token.raw.length);
36860 tokens.push(token);
36862 } // top-level paragraph
36865 if (top && (token = this.tokenizer.paragraph(src))) {
36866 src = src.substring(token.raw.length);
36867 tokens.push(token);
36872 if (token = this.tokenizer.text(src, tokens)) {
36873 src = src.substring(token.raw.length);
36876 tokens.push(token);
36878 lastToken = tokens[tokens.length - 1];
36879 lastToken.raw += '\n' + token.raw;
36880 lastToken.text += '\n' + token.text;
36887 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
36889 if (this.options.silent) {
36890 console.error(errMsg);
36893 throw new Error(errMsg);
36902 value: function inline(tokens) {
36903 var i, j, k, l2, row, token;
36904 var l = tokens.length;
36906 for (i = 0; i < l; i++) {
36909 switch (token.type) {
36915 this.inlineTokens(token.text, token.tokens);
36926 l2 = token.header.length;
36928 for (j = 0; j < l2; j++) {
36929 token.tokens.header[j] = [];
36930 this.inlineTokens(token.header[j], token.tokens.header[j]);
36934 l2 = token.cells.length;
36936 for (j = 0; j < l2; j++) {
36937 row = token.cells[j];
36938 token.tokens.cells[j] = [];
36940 for (k = 0; k < row.length; k++) {
36941 token.tokens.cells[j][k] = [];
36942 this.inlineTokens(row[k], token.tokens.cells[j][k]);
36951 this.inline(token.tokens);
36957 l2 = token.items.length;
36959 for (j = 0; j < l2; j++) {
36960 this.inline(token.items[j].tokens);
36975 key: "inlineTokens",
36976 value: function inlineTokens(src) {
36977 var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
36978 var inLink = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
36979 var inRawBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
36980 var prevChar = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '';
36981 var token; // String with links masked to avoid interference with em and strong
36983 var maskedSrc = src;
36984 var match; // Mask out reflinks
36986 if (this.tokens.links) {
36987 var links = Object.keys(this.tokens.links);
36989 if (links.length > 0) {
36990 while ((match = this.tokenizer.rules.inline.reflinkSearch.exec(maskedSrc)) != null) {
36991 if (links.includes(match[0].slice(match[0].lastIndexOf('[') + 1, -1))) {
36992 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex);
36996 } // Mask out other blocks
36999 while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) {
37000 maskedSrc = maskedSrc.slice(0, match.index) + '[' + repeatString$1('a', match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);
37005 if (token = this.tokenizer.escape(src)) {
37006 src = src.substring(token.raw.length);
37007 tokens.push(token);
37012 if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
37013 src = src.substring(token.raw.length);
37014 inLink = token.inLink;
37015 inRawBlock = token.inRawBlock;
37016 tokens.push(token);
37021 if (token = this.tokenizer.link(src)) {
37022 src = src.substring(token.raw.length);
37024 if (token.type === 'link') {
37025 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
37028 tokens.push(token);
37030 } // reflink, nolink
37033 if (token = this.tokenizer.reflink(src, this.tokens.links)) {
37034 src = src.substring(token.raw.length);
37036 if (token.type === 'link') {
37037 token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
37040 tokens.push(token);
37045 if (token = this.tokenizer.strong(src, maskedSrc, prevChar)) {
37046 src = src.substring(token.raw.length);
37047 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37048 tokens.push(token);
37053 if (token = this.tokenizer.em(src, maskedSrc, prevChar)) {
37054 src = src.substring(token.raw.length);
37055 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37056 tokens.push(token);
37061 if (token = this.tokenizer.codespan(src)) {
37062 src = src.substring(token.raw.length);
37063 tokens.push(token);
37068 if (token = this.tokenizer.br(src)) {
37069 src = src.substring(token.raw.length);
37070 tokens.push(token);
37075 if (token = this.tokenizer.del(src)) {
37076 src = src.substring(token.raw.length);
37077 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
37078 tokens.push(token);
37083 if (token = this.tokenizer.autolink(src, mangle)) {
37084 src = src.substring(token.raw.length);
37085 tokens.push(token);
37090 if (!inLink && (token = this.tokenizer.url(src, mangle))) {
37091 src = src.substring(token.raw.length);
37092 tokens.push(token);
37097 if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
37098 src = src.substring(token.raw.length);
37099 prevChar = token.raw.slice(-1);
37100 tokens.push(token);
37105 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
37107 if (this.options.silent) {
37108 console.error(errMsg);
37111 throw new Error(errMsg);
37122 * Static Lex Method
37124 value: function lex(src, options) {
37125 var lexer = new Lexer(options);
37126 return lexer.lex(src);
37129 * Static Lex Inline Method
37134 value: function lexInline(src, options) {
37135 var lexer = new Lexer(options);
37136 return lexer.inlineTokens(src);
37140 get: function get() {
37151 var defaults$3 = defaults.defaults;
37152 var cleanUrl$1 = helpers.cleanUrl,
37153 escape$2 = helpers.escape;
37158 var Renderer_1 = /*#__PURE__*/function () {
37159 function Renderer(options) {
37160 _classCallCheck(this, Renderer);
37162 this.options = options || defaults$3;
37165 _createClass(Renderer, [{
37167 value: function code(_code, infostring, escaped) {
37168 var lang = (infostring || '').match(/\S*/)[0];
37170 if (this.options.highlight) {
37171 var out = this.options.highlight(_code, lang);
37173 if (out != null && out !== _code) {
37180 return '<pre><code>' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
37183 return '<pre><code class="' + this.options.langPrefix + escape$2(lang, true) + '">' + (escaped ? _code : escape$2(_code, true)) + '</code></pre>\n';
37187 value: function blockquote(quote) {
37188 return '<blockquote>\n' + quote + '</blockquote>\n';
37192 value: function html(_html) {
37197 value: function heading(text, level, raw, slugger) {
37198 if (this.options.headerIds) {
37199 return '<h' + level + ' id="' + this.options.headerPrefix + slugger.slug(raw) + '">' + text + '</h' + level + '>\n';
37203 return '<h' + level + '>' + text + '</h' + level + '>\n';
37207 value: function hr() {
37208 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
37212 value: function list(body, ordered, start) {
37213 var type = ordered ? 'ol' : 'ul',
37214 startatt = ordered && start !== 1 ? ' start="' + start + '"' : '';
37215 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
37219 value: function listitem(text) {
37220 return '<li>' + text + '</li>\n';
37224 value: function checkbox(checked) {
37225 return '<input ' + (checked ? 'checked="" ' : '') + 'disabled="" type="checkbox"' + (this.options.xhtml ? ' /' : '') + '> ';
37229 value: function paragraph(text) {
37230 return '<p>' + text + '</p>\n';
37234 value: function table(header, body) {
37235 if (body) body = '<tbody>' + body + '</tbody>';
37236 return '<table>\n' + '<thead>\n' + header + '</thead>\n' + body + '</table>\n';
37240 value: function tablerow(content) {
37241 return '<tr>\n' + content + '</tr>\n';
37245 value: function tablecell(content, flags) {
37246 var type = flags.header ? 'th' : 'td';
37247 var tag = flags.align ? '<' + type + ' align="' + flags.align + '">' : '<' + type + '>';
37248 return tag + content + '</' + type + '>\n';
37249 } // span level renderer
37253 value: function strong(text) {
37254 return '<strong>' + text + '</strong>';
37258 value: function em(text) {
37259 return '<em>' + text + '</em>';
37263 value: function codespan(text) {
37264 return '<code>' + text + '</code>';
37268 value: function br() {
37269 return this.options.xhtml ? '<br/>' : '<br>';
37273 value: function del(text) {
37274 return '<del>' + text + '</del>';
37278 value: function link(href, title, text) {
37279 href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
37281 if (href === null) {
37285 var out = '<a href="' + escape$2(href) + '"';
37288 out += ' title="' + title + '"';
37291 out += '>' + text + '</a>';
37296 value: function image(href, title, text) {
37297 href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
37299 if (href === null) {
37303 var out = '<img src="' + href + '" alt="' + text + '"';
37306 out += ' title="' + title + '"';
37309 out += this.options.xhtml ? '/>' : '>';
37314 value: function text(_text) {
37324 * returns only the textual part of the token
37326 var TextRenderer_1 = /*#__PURE__*/function () {
37327 function TextRenderer() {
37328 _classCallCheck(this, TextRenderer);
37331 _createClass(TextRenderer, [{
37333 // no need for block level renderers
37334 value: function strong(text) {
37339 value: function em(text) {
37344 value: function codespan(text) {
37349 value: function del(text) {
37354 value: function html(text) {
37359 value: function text(_text) {
37364 value: function link(href, title, text) {
37369 value: function image(href, title, text) {
37374 value: function br() {
37379 return TextRenderer;
37383 * Slugger generates header id
37385 var Slugger_1 = /*#__PURE__*/function () {
37386 function Slugger() {
37387 _classCallCheck(this, Slugger);
37392 _createClass(Slugger, [{
37394 value: function serialize(value) {
37395 return value.toLowerCase().trim() // remove html tags
37396 .replace(/<[!\/a-z].*?>/ig, '') // remove unwanted chars
37397 .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '').replace(/\s/g, '-');
37400 * Finds the next safe (unique) slug to use
37404 key: "getNextSafeSlug",
37405 value: function getNextSafeSlug(originalSlug, isDryRun) {
37406 var slug = originalSlug;
37407 var occurenceAccumulator = 0;
37409 if (this.seen.hasOwnProperty(slug)) {
37410 occurenceAccumulator = this.seen[originalSlug];
37413 occurenceAccumulator++;
37414 slug = originalSlug + '-' + occurenceAccumulator;
37415 } while (this.seen.hasOwnProperty(slug));
37419 this.seen[originalSlug] = occurenceAccumulator;
37420 this.seen[slug] = 0;
37426 * Convert string to unique id
37427 * @param {object} options
37428 * @param {boolean} options.dryrun Generates the next unique slug without updating the internal accumulator.
37433 value: function slug(value) {
37434 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
37435 var slug = this.serialize(value);
37436 return this.getNextSafeSlug(slug, options.dryrun);
37443 var defaults$4 = defaults.defaults;
37444 var unescape$2 = helpers.unescape;
37446 * Parsing & Compiling
37449 var Parser_1 = /*#__PURE__*/function () {
37450 function Parser(options) {
37451 _classCallCheck(this, Parser);
37453 this.options = options || defaults$4;
37454 this.options.renderer = this.options.renderer || new Renderer_1();
37455 this.renderer = this.options.renderer;
37456 this.renderer.options = this.options;
37457 this.textRenderer = new TextRenderer_1();
37458 this.slugger = new Slugger_1();
37461 * Static Parse Method
37465 _createClass(Parser, [{
37471 value: function parse(tokens) {
37472 var top = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
37492 var l = tokens.length;
37494 for (i = 0; i < l; i++) {
37497 switch (token.type) {
37505 out += this.renderer.hr();
37511 out += this.renderer.heading(this.parseInline(token.tokens), token.depth, unescape$2(this.parseInline(token.tokens, this.textRenderer)), this.slugger);
37517 out += this.renderer.code(token.text, token.lang, token.escaped);
37523 header = ''; // header
37526 l2 = token.header.length;
37528 for (j = 0; j < l2; j++) {
37529 cell += this.renderer.tablecell(this.parseInline(token.tokens.header[j]), {
37531 align: token.align[j]
37535 header += this.renderer.tablerow(cell);
37537 l2 = token.cells.length;
37539 for (j = 0; j < l2; j++) {
37540 row = token.tokens.cells[j];
37544 for (k = 0; k < l3; k++) {
37545 cell += this.renderer.tablecell(this.parseInline(row[k]), {
37547 align: token.align[k]
37551 body += this.renderer.tablerow(cell);
37554 out += this.renderer.table(header, body);
37560 body = this.parse(token.tokens);
37561 out += this.renderer.blockquote(body);
37567 ordered = token.ordered;
37568 start = token.start;
37569 loose = token.loose;
37570 l2 = token.items.length;
37573 for (j = 0; j < l2; j++) {
37574 item = token.items[j];
37575 checked = item.checked;
37580 checkbox = this.renderer.checkbox(checked);
37583 if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
37584 item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
37586 if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
37587 item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
37590 item.tokens.unshift({
37596 itemBody += checkbox;
37600 itemBody += this.parse(item.tokens, loose);
37601 body += this.renderer.listitem(itemBody, task, checked);
37604 out += this.renderer.list(body, ordered, start);
37610 // TODO parse inline content if parameter markdown=1
37611 out += this.renderer.html(token.text);
37617 out += this.renderer.paragraph(this.parseInline(token.tokens));
37623 body = token.tokens ? this.parseInline(token.tokens) : token.text;
37625 while (i + 1 < l && tokens[i + 1].type === 'text') {
37626 token = tokens[++i];
37627 body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
37630 out += top ? this.renderer.paragraph(body) : body;
37636 var errMsg = 'Token with "' + token.type + '" type was not found.';
37638 if (this.options.silent) {
37639 console.error(errMsg);
37642 throw new Error(errMsg);
37651 * Parse Inline Tokens
37655 key: "parseInline",
37656 value: function parseInline(tokens, renderer) {
37657 renderer = renderer || this.renderer;
37661 var l = tokens.length;
37663 for (i = 0; i < l; i++) {
37666 switch (token.type) {
37669 out += renderer.text(token.text);
37675 out += renderer.html(token.text);
37681 out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
37687 out += renderer.image(token.href, token.title, token.text);
37693 out += renderer.strong(this.parseInline(token.tokens, renderer));
37699 out += renderer.em(this.parseInline(token.tokens, renderer));
37705 out += renderer.codespan(token.text);
37711 out += renderer.br();
37717 out += renderer.del(this.parseInline(token.tokens, renderer));
37723 out += renderer.text(token.text);
37729 var errMsg = 'Token with "' + token.type + '" type was not found.';
37731 if (this.options.silent) {
37732 console.error(errMsg);
37735 throw new Error(errMsg);
37745 value: function parse(tokens, options) {
37746 var parser = new Parser(options);
37747 return parser.parse(tokens);
37750 * Static Parse Inline Method
37754 key: "parseInline",
37755 value: function parseInline(tokens, options) {
37756 var parser = new Parser(options);
37757 return parser.parseInline(tokens);
37764 var merge$3 = helpers.merge,
37765 checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation,
37766 escape$3 = helpers.escape;
37767 var getDefaults = defaults.getDefaults,
37768 changeDefaults = defaults.changeDefaults,
37769 defaults$5 = defaults.defaults;
37774 function marked(src, opt, callback) {
37775 // throw error in case of non string input
37776 if (typeof src === 'undefined' || src === null) {
37777 throw new Error('marked(): input parameter is undefined or null');
37780 if (typeof src !== 'string') {
37781 throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
37784 if (typeof opt === 'function') {
37789 opt = merge$3({}, marked.defaults, opt || {});
37790 checkSanitizeDeprecation$1(opt);
37793 var highlight = opt.highlight;
37797 tokens = Lexer_1.lex(src, opt);
37799 return callback(e);
37802 var done = function done(err) {
37807 out = Parser_1.parse(tokens, opt);
37813 opt.highlight = highlight;
37814 return err ? callback(err) : callback(null, out);
37817 if (!highlight || highlight.length < 3) {
37821 delete opt.highlight;
37822 if (!tokens.length) return done();
37824 marked.walkTokens(tokens, function (token) {
37825 if (token.type === 'code') {
37827 setTimeout(function () {
37828 highlight(token.text, token.lang, function (err, code) {
37833 if (code != null && code !== token.text) {
37835 token.escaped = true;
37840 if (pending === 0) {
37848 if (pending === 0) {
37856 var _tokens = Lexer_1.lex(src, opt);
37858 if (opt.walkTokens) {
37859 marked.walkTokens(_tokens, opt.walkTokens);
37862 return Parser_1.parse(_tokens, opt);
37864 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
37867 return '<p>An error occurred:</p><pre>' + escape$3(e.message + '', true) + '</pre>';
37878 marked.options = marked.setOptions = function (opt) {
37879 merge$3(marked.defaults, opt);
37880 changeDefaults(marked.defaults);
37884 marked.getDefaults = getDefaults;
37885 marked.defaults = defaults$5;
37890 marked.use = function (extension) {
37891 var opts = merge$3({}, extension);
37893 if (extension.renderer) {
37895 var renderer = marked.defaults.renderer || new Renderer_1();
37897 var _loop = function _loop(prop) {
37898 var prevRenderer = renderer[prop];
37900 renderer[prop] = function () {
37901 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
37902 args[_key] = arguments[_key];
37905 var ret = extension.renderer[prop].apply(renderer, args);
37907 if (ret === false) {
37908 ret = prevRenderer.apply(renderer, args);
37915 for (var prop in extension.renderer) {
37919 opts.renderer = renderer;
37923 if (extension.tokenizer) {
37925 var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
37927 var _loop2 = function _loop2(prop) {
37928 var prevTokenizer = tokenizer[prop];
37930 tokenizer[prop] = function () {
37931 for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
37932 args[_key2] = arguments[_key2];
37935 var ret = extension.tokenizer[prop].apply(tokenizer, args);
37937 if (ret === false) {
37938 ret = prevTokenizer.apply(tokenizer, args);
37945 for (var prop in extension.tokenizer) {
37949 opts.tokenizer = tokenizer;
37953 if (extension.walkTokens) {
37954 var walkTokens = marked.defaults.walkTokens;
37956 opts.walkTokens = function (token) {
37957 extension.walkTokens(token);
37965 marked.setOptions(opts);
37968 * Run callback for every token
37972 marked.walkTokens = function (tokens, callback) {
37973 var _iterator = _createForOfIteratorHelper(tokens),
37977 for (_iterator.s(); !(_step = _iterator.n()).done;) {
37978 var token = _step.value;
37981 switch (token.type) {
37984 var _iterator2 = _createForOfIteratorHelper(token.tokens.header),
37988 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
37989 var cell = _step2.value;
37990 marked.walkTokens(cell, callback);
37998 var _iterator3 = _createForOfIteratorHelper(token.tokens.cells),
38002 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
38003 var row = _step3.value;
38005 var _iterator4 = _createForOfIteratorHelper(row),
38009 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
38010 var _cell = _step4.value;
38011 marked.walkTokens(_cell, callback);
38030 marked.walkTokens(token.items, callback);
38036 if (token.tokens) {
38037 marked.walkTokens(token.tokens, callback);
38053 marked.parseInline = function (src, opt) {
38054 // throw error in case of non string input
38055 if (typeof src === 'undefined' || src === null) {
38056 throw new Error('marked.parseInline(): input parameter is undefined or null');
38059 if (typeof src !== 'string') {
38060 throw new Error('marked.parseInline(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected');
38063 opt = merge$3({}, marked.defaults, opt || {});
38064 checkSanitizeDeprecation$1(opt);
38067 var tokens = Lexer_1.lexInline(src, opt);
38069 if (opt.walkTokens) {
38070 marked.walkTokens(tokens, opt.walkTokens);
38073 return Parser_1.parseInline(tokens, opt);
38075 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
38078 return '<p>An error occurred:</p><pre>' + escape$3(e.message + '', true) + '</pre>';
38089 marked.Parser = Parser_1;
38090 marked.parser = Parser_1.parse;
38091 marked.Renderer = Renderer_1;
38092 marked.TextRenderer = TextRenderer_1;
38093 marked.Lexer = Lexer_1;
38094 marked.lexer = Lexer_1.lex;
38095 marked.Tokenizer = Tokenizer_1;
38096 marked.Slugger = Slugger_1;
38097 marked.parse = marked;
38098 var marked_1 = marked;
38100 var tiler$2 = utilTiler();
38101 var dispatch$3 = dispatch('loaded');
38102 var _tileZoom$2 = 14;
38103 var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
38104 var _osmoseData = {
38107 }; // This gets reassigned if reset
38111 function abortRequest$2(controller) {
38113 controller.abort();
38117 function abortUnwantedRequests$2(cache, tiles) {
38118 Object.keys(cache.inflightTile).forEach(function (k) {
38119 var wanted = tiles.find(function (tile) {
38120 return k === tile.id;
38124 abortRequest$2(cache.inflightTile[k]);
38125 delete cache.inflightTile[k];
38130 function encodeIssueRtree$2(d) {
38138 } // Replace or remove QAItem from rtree
38141 function updateRtree$2(item, replace) {
38142 _cache$2.rtree.remove(item, function (a, b) {
38143 return a.data.id === b.data.id;
38147 _cache$2.rtree.insert(item);
38149 } // Issues shouldn't obscure each other
38152 function preventCoincident$1(loc) {
38153 var coincident = false;
38156 // first time, move marker up. after that, move marker right.
38157 var delta = coincident ? [0.00001, 0] : [0, 0.00001];
38158 loc = geoVecAdd(loc, delta);
38159 var bbox = geoExtent(loc).bbox();
38160 coincident = _cache$2.rtree.search(bbox).length;
38161 } while (coincident);
38166 var serviceOsmose = {
38168 init: function init() {
38169 _mainFileFetcher.get('qa_data').then(function (d) {
38170 _osmoseData = d.osmose;
38171 _osmoseData.items = Object.keys(d.osmose.icons).map(function (s) {
38172 return s.split('-')[0];
38173 }).reduce(function (unique, item) {
38174 return unique.indexOf(item) !== -1 ? unique : [].concat(_toConsumableArray(unique), [item]);
38182 this.event = utilRebind(this, dispatch$3, 'on');
38184 reset: function reset() {
38189 Object.values(_cache$2.inflightTile).forEach(abortRequest$2); // Strings and colors are static and should not be re-populated
38191 _strings = _cache$2.strings;
38192 _colors = _cache$2.colors;
38201 rtree: new RBush(),
38206 loadIssues: function loadIssues(projection) {
38210 // Tiles return a maximum # of issues
38211 // So we want to filter our request for only types iD supports
38212 item: _osmoseData.items
38213 }; // determine the needed tiles to cover the view
38215 var tiles = tiler$2.zoomExtent([_tileZoom$2, _tileZoom$2]).getTiles(projection); // abort inflight requests that are no longer needed
38217 abortUnwantedRequests$2(_cache$2, tiles); // issue new requests..
38219 tiles.forEach(function (tile) {
38220 if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) return;
38222 var _tile$xyz = _slicedToArray(tile.xyz, 3),
38227 var url = "".concat(_osmoseUrlRoot, "/issues/").concat(z, "/").concat(x, "/").concat(y, ".json?") + utilQsString(params);
38228 var controller = new AbortController();
38229 _cache$2.inflightTile[tile.id] = controller;
38231 signal: controller.signal
38232 }).then(function (data) {
38233 delete _cache$2.inflightTile[tile.id];
38234 _cache$2.loadedTile[tile.id] = true;
38236 if (data.features) {
38237 data.features.forEach(function (issue) {
38238 var _issue$properties = issue.properties,
38239 item = _issue$properties.item,
38240 cl = _issue$properties["class"],
38241 id = _issue$properties.uuid;
38242 /* Osmose issues are uniquely identified by a unique
38243 `item` and `class` combination (both integer values) */
38245 var itemType = "".concat(item, "-").concat(cl); // Filter out unsupported issue types (some are too specific or advanced)
38247 if (itemType in _osmoseData.icons) {
38248 var loc = issue.geometry.coordinates; // lon, lat
38250 loc = preventCoincident$1(loc);
38251 var d = new QAItem(loc, _this, itemType, id, {
38253 }); // Setting elems here prevents UI detail requests
38255 if (item === 8300 || item === 8360) {
38259 _cache$2.data[d.id] = d;
38261 _cache$2.rtree.insert(encodeIssueRtree$2(d));
38266 dispatch$3.call('loaded');
38267 })["catch"](function () {
38268 delete _cache$2.inflightTile[tile.id];
38269 _cache$2.loadedTile[tile.id] = true;
38273 loadIssueDetail: function loadIssueDetail(issue) {
38276 // Issue details only need to be fetched once
38277 if (issue.elems !== undefined) {
38278 return Promise.resolve(issue);
38281 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "?langs=").concat(_mainLocalizer.localeCode());
38283 var cacheDetails = function cacheDetails(data) {
38284 // Associated elements used for highlighting
38285 // Assign directly for immediate use in the callback
38286 issue.elems = data.elems.map(function (e) {
38287 return e.type.substring(0, 1) + e.id;
38288 }); // Some issues have instance specific detail in a subtitle
38290 issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
38292 _this2.replaceItem(issue);
38295 return d3_json(url).then(cacheDetails).then(function () {
38299 loadStrings: function loadStrings() {
38300 var locale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _mainLocalizer.localeCode();
38301 var items = Object.keys(_osmoseData.icons);
38303 if (locale in _cache$2.strings && Object.keys(_cache$2.strings[locale]).length === items.length) {
38304 return Promise.resolve(_cache$2.strings[locale]);
38305 } // May be partially populated already if some requests were successful
38308 if (!(locale in _cache$2.strings)) {
38309 _cache$2.strings[locale] = {};
38310 } // Only need to cache strings for supported issue types
38311 // Using multiple individual item + class requests to reduce fetched data size
38314 var allRequests = items.map(function (itemType) {
38315 // No need to request data we already have
38316 if (itemType in _cache$2.strings[locale]) return null;
38318 var cacheData = function cacheData(data) {
38319 // Bunch of nested single value arrays of objects
38320 var _data$categories = _slicedToArray(data.categories, 1),
38321 _data$categories$ = _data$categories[0],
38322 cat = _data$categories$ === void 0 ? {
38324 } : _data$categories$;
38326 var _cat$items = _slicedToArray(cat.items, 1),
38327 _cat$items$ = _cat$items[0],
38328 item = _cat$items$ === void 0 ? {
38332 var _item$class = _slicedToArray(item["class"], 1),
38333 _item$class$ = _item$class[0],
38334 cl = _item$class$ === void 0 ? null : _item$class$; // If null default value is reached, data wasn't as expected (or was empty)
38338 /* eslint-disable no-console */
38339 console.log("Osmose strings request (".concat(itemType, ") had unexpected data"));
38340 /* eslint-enable no-console */
38343 } // Cache served item colors to automatically style issue markers later
38346 var itemInt = item.item,
38347 color = item.color;
38349 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
38350 _cache$2.colors[itemInt] = color;
38351 } // Value of root key will be null if no string exists
38352 // If string exists, value is an object with key 'auto' for string
38355 var title = cl.title,
38356 detail = cl.detail,
38358 trap = cl.trap; // Osmose titles shouldn't contain markdown
38360 var issueStrings = {};
38361 if (title) issueStrings.title = title.auto;
38362 if (detail) issueStrings.detail = marked_1(detail.auto);
38363 if (trap) issueStrings.trap = marked_1(trap.auto);
38364 if (fix) issueStrings.fix = marked_1(fix.auto);
38365 _cache$2.strings[locale][itemType] = issueStrings;
38368 var _itemType$split = itemType.split('-'),
38369 _itemType$split2 = _slicedToArray(_itemType$split, 2),
38370 item = _itemType$split2[0],
38371 cl = _itemType$split2[1]; // Osmose API falls back to English strings where untranslated or if locale doesn't exist
38374 var url = "".concat(_osmoseUrlRoot, "/items/").concat(item, "/class/").concat(cl, "?langs=").concat(locale);
38375 return d3_json(url).then(cacheData);
38376 }).filter(Boolean);
38377 return Promise.all(allRequests).then(function () {
38378 return _cache$2.strings[locale];
38381 getStrings: function getStrings(itemType) {
38382 var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _mainLocalizer.localeCode();
38383 // No need to fallback to English, Osmose API handles this for us
38384 return locale in _cache$2.strings ? _cache$2.strings[locale][itemType] : {};
38386 getColor: function getColor(itemType) {
38387 return itemType in _cache$2.colors ? _cache$2.colors[itemType] : '#FFFFFF';
38389 postUpdate: function postUpdate(issue, callback) {
38392 if (_cache$2.inflightPost[issue.id]) {
38394 message: 'Issue update already inflight',
38397 } // UI sets the status to either 'done' or 'false'
38400 var url = "".concat(_osmoseUrlRoot, "/issue/").concat(issue.id, "/").concat(issue.newStatus);
38401 var controller = new AbortController();
38403 var after = function after() {
38404 delete _cache$2.inflightPost[issue.id];
38406 _this3.removeItem(issue);
38408 if (issue.newStatus === 'done') {
38409 // Keep track of the number of issues closed per `item` to tag the changeset
38410 if (!(issue.item in _cache$2.closed)) {
38411 _cache$2.closed[issue.item] = 0;
38414 _cache$2.closed[issue.item] += 1;
38417 if (callback) callback(null, issue);
38420 _cache$2.inflightPost[issue.id] = controller;
38422 signal: controller.signal
38423 }).then(after)["catch"](function (err) {
38424 delete _cache$2.inflightPost[issue.id];
38425 if (callback) callback(err.message);
38428 // Get all cached QAItems covering the viewport
38429 getItems: function getItems(projection) {
38430 var viewport = projection.clipExtent();
38431 var min = [viewport[0][0], viewport[1][1]];
38432 var max = [viewport[1][0], viewport[0][1]];
38433 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
38434 return _cache$2.rtree.search(bbox).map(function (d) {
38438 // Get a QAItem from cache
38439 // NOTE: Don't change method name until UI v3 is merged
38440 getError: function getError(id) {
38441 return _cache$2.data[id];
38443 // get the name of the icon to display for this item
38444 getIcon: function getIcon(itemType) {
38445 return _osmoseData.icons[itemType];
38447 // Replace a single QAItem in the cache
38448 replaceItem: function replaceItem(item) {
38449 if (!(item instanceof QAItem) || !item.id) return;
38450 _cache$2.data[item.id] = item;
38451 updateRtree$2(encodeIssueRtree$2(item), true); // true = replace
38455 // Remove a single QAItem from the cache
38456 removeItem: function removeItem(item) {
38457 if (!(item instanceof QAItem) || !item.id) return;
38458 delete _cache$2.data[item.id];
38459 updateRtree$2(encodeIssueRtree$2(item), false); // false = remove
38461 // Used to populate `closed:osmose:*` changeset tags
38462 getClosedCounts: function getClosedCounts() {
38463 return _cache$2.closed;
38465 itemURL: function itemURL(item) {
38466 return "https://osmose.openstreetmap.fr/en/error/".concat(item.id);
38470 var apibase = 'https://a.mapillary.com/v3/';
38471 var viewercss = 'mapillary-js/mapillary.min.css';
38472 var viewerjs = 'mapillary-js/mapillary.min.js';
38473 var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
38474 var mapFeatureConfig = {
38475 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(',')
38477 var maxResults = 1000;
38479 var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
38480 var dispatch$4 = dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'nodeChanged');
38481 var _mlyFallback = false;
38487 var _mlyActiveImage;
38489 var _mlySelectedImageKey;
38493 var _mlyViewerFilter = ['all'];
38495 var _loadViewerPromise;
38497 var _mlyHighlightedDetection;
38499 var _mlyShowFeatureDetections = false;
38500 var _mlyShowSignDetections = false;
38502 function abortRequest$3(controller) {
38503 controller.abort();
38506 function loadTiles(which, url, projection) {
38507 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
38508 var tiles = tiler$3.getTiles(projection); // abort inflight requests that are no longer needed
38510 var cache = _mlyCache[which];
38511 Object.keys(cache.inflight).forEach(function (k) {
38512 var wanted = tiles.find(function (tile) {
38513 return k.indexOf(tile.id + ',') === 0;
38517 abortRequest$3(cache.inflight[k]);
38518 delete cache.inflight[k];
38521 tiles.forEach(function (tile) {
38522 loadNextTilePage(which, currZoom, url, tile);
38526 function loadNextTilePage(which, currZoom, url, tile) {
38527 var cache = _mlyCache[which];
38528 var rect = tile.extent.rectangle();
38529 var maxPages = maxPageAtZoom(currZoom);
38530 var nextPage = cache.nextPage[tile.id] || 0;
38531 var nextURL = cache.nextURL[tile.id] || url + utilQsString({
38532 per_page: maxResults,
38534 client_id: clientId,
38535 bbox: [rect[0], rect[1], rect[2], rect[3]].join(',')
38537 if (nextPage > maxPages) return;
38538 var id = tile.id + ',' + String(nextPage);
38539 if (cache.loaded[id] || cache.inflight[id]) return;
38540 var controller = new AbortController();
38541 cache.inflight[id] = controller;
38544 signal: controller.signal,
38546 'Content-Type': 'application/json'
38549 fetch(nextURL, options).then(function (response) {
38550 if (!response.ok) {
38551 throw new Error(response.status + ' ' + response.statusText);
38554 var linkHeader = response.headers.get('Link');
38557 var pagination = parsePagination(linkHeader);
38559 if (pagination.next) {
38560 cache.nextURL[tile.id] = pagination.next;
38564 return response.json();
38565 }).then(function (data) {
38566 cache.loaded[id] = true;
38567 delete cache.inflight[id];
38569 if (!data || !data.features || !data.features.length) {
38570 throw new Error('No Data');
38573 var features = data.features.map(function (feature) {
38574 var loc = feature.geometry.coordinates;
38575 var d; // An image (shown as a green dot on the map) is a single street photo with extra
38576 // information such as location, camera angle (CA), camera model, and so on.
38577 // Each image feature is a GeoJSON Point
38579 if (which === 'images') {
38582 key: feature.properties.key,
38583 ca: feature.properties.ca,
38584 captured_at: feature.properties.captured_at,
38585 captured_by: feature.properties.username,
38586 pano: feature.properties.pano
38588 cache.forImageKey[d.key] = d; // cache imageKey -> image
38589 // Mapillary organizes images as sequences. A sequence of images are continuously captured
38590 // by a user at a give time. Sequences are shown on the map as green lines.
38591 // Each sequence feature is a GeoJSON LineString
38592 } else if (which === 'sequences') {
38593 var sequenceKey = feature.properties.key;
38594 cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString
38596 feature.properties.coordinateProperties.image_keys.forEach(function (imageKey) {
38597 cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey
38599 return false; // because no `d` data worth loading into an rbush
38600 // A map feature is a real world object that can be shown on a map. It could be any object
38601 // recognized from images, manually added in images, or added on the map.
38602 // Each map feature is a GeoJSON Point (located where the feature is)
38603 } else if (which === 'map_features' || which === 'points') {
38606 key: feature.properties.key,
38607 value: feature.properties.value,
38608 detections: feature.properties.detections,
38609 direction: feature.properties.direction,
38610 accuracy: feature.properties.accuracy,
38611 first_seen_at: feature.properties.first_seen_at,
38612 last_seen_at: feature.properties.last_seen_at
38623 }).filter(Boolean);
38625 if (cache.rtree && features) {
38626 cache.rtree.load(features);
38629 if (data.features.length === maxResults) {
38630 // more pages to load
38631 cache.nextPage[tile.id] = nextPage + 1;
38632 loadNextTilePage(which, currZoom, url, tile);
38634 cache.nextPage[tile.id] = Infinity; // no more pages to load
38637 if (which === 'images' || which === 'sequences') {
38638 dispatch$4.call('loadedImages');
38639 } else if (which === 'map_features') {
38640 dispatch$4.call('loadedSigns');
38641 } else if (which === 'points') {
38642 dispatch$4.call('loadedMapFeatures');
38644 })["catch"](function () {
38645 cache.loaded[id] = true;
38646 delete cache.inflight[id];
38650 function loadData(which, url) {
38651 var cache = _mlyCache[which];
38655 'Content-Type': 'application/json'
38658 var nextUrl = url + '&client_id=' + clientId;
38659 return fetch(nextUrl, options).then(function (response) {
38660 if (!response.ok) {
38661 throw new Error(response.status + ' ' + response.statusText);
38664 return response.json();
38665 }).then(function (data) {
38666 if (!data || !data.features || !data.features.length) {
38667 throw new Error('No Data');
38670 data.features.forEach(function (feature) {
38673 if (which === 'image_detections') {
38675 key: feature.properties.key,
38676 image_key: feature.properties.image_key,
38677 value: feature.properties.value,
38678 shape: feature.properties.shape
38681 if (!cache.forImageKey[d.image_key]) {
38682 cache.forImageKey[d.image_key] = [];
38685 cache.forImageKey[d.image_key].push(d);
38691 function maxPageAtZoom(z) {
38692 if (z < 15) return 2;
38693 if (z === 15) return 5;
38694 if (z === 16) return 10;
38695 if (z === 17) return 20;
38696 if (z === 18) return 40;
38697 if (z > 18) return 80;
38698 } // extract links to pages of API results
38701 function parsePagination(links) {
38702 return links.split(',').map(function (rel) {
38703 var elements = rel.split(';');
38705 if (elements.length === 2) {
38706 return [/<(.+)>/.exec(elements[0])[1], /rel="(.+)"/.exec(elements[1])[1]];
38710 }).reduce(function (pagination, val) {
38711 pagination[val[1]] = val[0];
38714 } // partition viewport into higher zoom tiles
38717 function partitionViewport(projection) {
38718 var z = geoScaleToZoom(projection.scale());
38719 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
38721 var tiler = utilTiler().zoomExtent([z2, z2]);
38722 return tiler.getTiles(projection).map(function (tile) {
38723 return tile.extent;
38725 } // no more than `limit` results per partition.
38728 function searchLimited(limit, projection, rtree) {
38729 limit = limit || 5;
38730 return partitionViewport(projection).reduce(function (result, extent) {
38731 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
38734 return found.length ? result.concat(found) : result;
38738 var serviceMapillary = {
38739 init: function init() {
38744 this.event = utilRebind(this, dispatch$4, 'on');
38746 reset: function reset() {
38748 Object.values(_mlyCache.images.inflight).forEach(abortRequest$3);
38749 Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3);
38750 Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3);
38751 Object.values(_mlyCache.points.inflight).forEach(abortRequest$3);
38752 Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3);
38761 rtree: new RBush(),
38764 image_detections: {
38790 rtree: new RBush(),
38795 _mlySelectedImageKey = null;
38796 _mlyActiveImage = null;
38799 images: function images(projection) {
38801 return searchLimited(limit, projection, _mlyCache.images.rtree);
38803 signs: function signs(projection) {
38805 return searchLimited(limit, projection, _mlyCache.map_features.rtree);
38807 mapFeatures: function mapFeatures(projection) {
38809 return searchLimited(limit, projection, _mlyCache.points.rtree);
38811 cachedImage: function cachedImage(imageKey) {
38812 return _mlyCache.images.forImageKey[imageKey];
38814 sequences: function sequences(projection) {
38815 var viewport = projection.clipExtent();
38816 var min = [viewport[0][0], viewport[1][1]];
38817 var max = [viewport[1][0], viewport[0][1]];
38818 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
38819 var sequenceKeys = {}; // all sequences for images in viewport
38821 _mlyCache.images.rtree.search(bbox).forEach(function (d) {
38822 var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
38825 sequenceKeys[sequenceKey] = true;
38827 }); // Return lineStrings for the sequences
38830 return Object.keys(sequenceKeys).map(function (sequenceKey) {
38831 return _mlyCache.sequences.lineString[sequenceKey];
38834 signsSupported: function signsSupported() {
38837 loadImages: function loadImages(projection) {
38838 loadTiles('images', apibase + 'images?sort_by=key&', projection);
38839 loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection);
38841 loadSigns: function loadSigns(projection) {
38842 loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection);
38844 loadMapFeatures: function loadMapFeatures(projection) {
38845 loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
38847 ensureViewerLoaded: function ensureViewerLoaded(context) {
38848 if (_loadViewerPromise) return _loadViewerPromise; // add mly-wrapper
38850 var wrap = context.container().select('.photoviewer').selectAll('.mly-wrapper').data([0]);
38851 wrap.enter().append('div').attr('id', 'ideditor-mly').attr('class', 'photo-wrapper mly-wrapper').classed('hide', true);
38853 _loadViewerPromise = new Promise(function (resolve, reject) {
38854 var loadedCount = 0;
38856 function loaded() {
38857 loadedCount += 1; // wait until both files are loaded
38859 if (loadedCount === 2) resolve();
38862 var head = select('head'); // load mapillary-viewercss
38864 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 () {
38866 }); // load mapillary-viewerjs
38868 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 () {
38871 })["catch"](function () {
38872 _loadViewerPromise = null;
38873 }).then(function () {
38874 that.initViewer(context);
38876 return _loadViewerPromise;
38878 loadSignResources: function loadSignResources(context) {
38879 context.ui().svgDefs.addSprites(['mapillary-sprite'], false
38880 /* don't override colors */
38884 loadObjectResources: function loadObjectResources(context) {
38885 context.ui().svgDefs.addSprites(['mapillary-object-sprite'], false
38886 /* don't override colors */
38890 resetTags: function resetTags() {
38891 if (_mlyViewer && !_mlyFallback) {
38892 _mlyViewer.getComponent('tag').removeAll(); // remove previous detections
38896 showFeatureDetections: function showFeatureDetections(value) {
38897 _mlyShowFeatureDetections = value;
38899 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
38903 showSignDetections: function showSignDetections(value) {
38904 _mlyShowSignDetections = value;
38906 if (!_mlyShowFeatureDetections && !_mlyShowSignDetections) {
38910 filterViewer: function filterViewer(context) {
38911 var showsPano = context.photos().showsPanoramic();
38912 var showsFlat = context.photos().showsFlat();
38913 var fromDate = context.photos().fromDate();
38914 var toDate = context.photos().toDate();
38915 var usernames = context.photos().usernames();
38916 var filter = ['all'];
38917 if (!showsPano) filter.push(['==', 'pano', false]);
38918 if (!showsFlat && showsPano) filter.push(['==', 'pano', true]);
38919 if (usernames && usernames.length) filter.push(['==', 'username', usernames[0]]);
38922 var fromTimestamp = new Date(fromDate).getTime();
38923 filter.push(['>=', 'capturedAt', fromTimestamp]);
38927 var toTimestamp = new Date(toDate).getTime();
38928 filter.push(['>=', 'capturedAt', toTimestamp]);
38932 _mlyViewer.setFilter(filter);
38935 _mlyViewerFilter = filter;
38938 showViewer: function showViewer(context) {
38939 var wrap = context.container().select('.photoviewer').classed('hide', false);
38940 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
38942 if (isHidden && _mlyViewer) {
38943 wrap.selectAll('.photo-wrapper:not(.mly-wrapper)').classed('hide', true);
38944 wrap.selectAll('.photo-wrapper.mly-wrapper').classed('hide', false);
38946 _mlyViewer.resize();
38951 hideViewer: function hideViewer(context) {
38952 _mlyActiveImage = null;
38953 _mlySelectedImageKey = null;
38955 if (!_mlyFallback && _mlyViewer) {
38956 _mlyViewer.getComponent('sequence').stop();
38959 var viewer = context.container().select('.photoviewer');
38960 if (!viewer.empty()) viewer.datum(null);
38961 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
38962 this.updateUrlImage(null);
38963 dispatch$4.call('nodeChanged');
38964 return this.setStyles(context, null, true);
38966 parsePagination: parsePagination,
38967 updateUrlImage: function updateUrlImage(imageKey) {
38968 if (!window.mocha) {
38969 var hash = utilStringQs(window.location.hash);
38972 hash.photo = 'mapillary/' + imageKey;
38977 window.location.replace('#' + utilQsString(hash, true));
38980 highlightDetection: function highlightDetection(detection) {
38982 _mlyHighlightedDetection = detection.detection_key;
38987 initViewer: function initViewer(context) {
38989 if (!window.Mapillary) return;
38991 baseImageSize: 320,
38997 }; // Disable components requiring WebGL support
38999 if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) {
39000 _mlyFallback = true;
39011 navigation: true // fallback
39016 _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts);
39018 _mlyViewer.on('nodechanged', nodeChanged);
39020 _mlyViewer.on('bearingchanged', bearingChanged);
39022 if (_mlyViewerFilter) {
39023 _mlyViewer.setFilter(_mlyViewerFilter);
39024 } // Register viewer resize handler
39027 context.ui().photoviewer.on('resize.mapillary', function () {
39028 if (_mlyViewer) _mlyViewer.resize();
39029 }); // nodeChanged: called after the viewer has changed images and is ready.
39031 // There is some logic here to batch up clicks into a _mlyClicks array
39032 // because the user might click on a lot of markers quickly and nodechanged
39033 // may be called out of order asynchronously.
39035 // Clicks are added to the array in `selectedImage` and removed here.
39038 function nodeChanged(node) {
39040 var clicks = _mlyClicks;
39041 var index = clicks.indexOf(node.key);
39042 var selectedKey = _mlySelectedImageKey;
39043 that.setActiveImage(node);
39046 // `nodechanged` initiated from clicking on a marker..
39047 clicks.splice(index, 1); // remove the click
39048 // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()`
39049 // one more time to update the detections and attribution..
39051 if (node.key === selectedKey) {
39052 that.selectImage(context, _mlySelectedImageKey, true);
39055 // `nodechanged` initiated from the Mapillary viewer controls..
39056 var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
39057 context.map().centerEase(loc);
39058 that.selectImage(context, node.key, true);
39061 dispatch$4.call('nodeChanged');
39064 function bearingChanged(e) {
39065 dispatch$4.call('bearingChanged', undefined, e);
39068 // Pass in the image key string as `imageKey`.
39069 // This allows images to be selected from places that dont have access
39070 // to the full image datum (like the street signs layer or the js viewer)
39071 selectImage: function selectImage(context, imageKey, fromViewer) {
39072 _mlySelectedImageKey = imageKey;
39073 this.updateUrlImage(imageKey);
39074 var d = _mlyCache.images.forImageKey[imageKey];
39075 var viewer = context.container().select('.photoviewer');
39076 if (!viewer.empty()) viewer.datum(d);
39077 imageKey = d && d.key || imageKey;
39079 if (!fromViewer && imageKey) {
39080 _mlyClicks.push(imageKey);
39083 this.setStyles(context, null, true);
39085 if (_mlyShowFeatureDetections) {
39086 this.updateDetections(imageKey, apibase + 'image_detections?layers=points&values=' + mapFeatureConfig.values + '&image_keys=' + imageKey);
39089 if (_mlyShowSignDetections) {
39090 this.updateDetections(imageKey, apibase + 'image_detections?layers=trafficsigns&image_keys=' + imageKey);
39093 if (_mlyViewer && imageKey) {
39094 _mlyViewer.moveToKey(imageKey)["catch"](function (e) {
39095 console.error('mly3', e);
39096 }); // eslint-disable-line no-console
39102 getActiveImage: function getActiveImage() {
39103 return _mlyActiveImage;
39105 getSelectedImageKey: function getSelectedImageKey() {
39106 return _mlySelectedImageKey;
39108 getSequenceKeyForImageKey: function getSequenceKeyForImageKey(imageKey) {
39109 return _mlyCache.sequences.forImageKey[imageKey];
39111 setActiveImage: function setActiveImage(node) {
39113 _mlyActiveImage = {
39114 ca: node.originalCA,
39116 loc: [node.originalLatLon.lon, node.originalLatLon.lat],
39120 _mlyActiveImage = null;
39123 // Updates the currently highlighted sequence and selected bubble.
39124 // Reset is only necessary when interacting with the viewport because
39125 // this implicitly changes the currently selected bubble/sequence
39126 setStyles: function setStyles(context, hovered, reset) {
39128 // reset all layers
39129 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false);
39130 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
39133 var hoveredImageKey = hovered && hovered.key;
39134 var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey);
39135 var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
39136 var hoveredImageKeys = hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys || [];
39137 var selectedImageKey = _mlySelectedImageKey;
39138 var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey);
39139 var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
39140 var selectedImageKeys = selectedLineString && selectedLineString.properties.coordinateProperties.image_keys || []; // highlight sibling viewfields on either the selected or the hovered sequences
39142 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
39143 context.container().selectAll('.layer-mapillary .viewfield-group').classed('highlighted', function (d) {
39144 return highlightedImageKeys.indexOf(d.key) !== -1;
39145 }).classed('hovered', function (d) {
39146 return d.key === hoveredImageKey;
39148 context.container().selectAll('.layer-mapillary .sequence').classed('highlighted', function (d) {
39149 return d.properties.key === hoveredSequenceKey;
39150 }).classed('currentView', function (d) {
39151 return d.properties.key === selectedSequenceKey;
39152 }); // update viewfields if needed
39154 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
39156 function viewfieldPath() {
39157 var d = this.parentNode.__data__;
39159 if (d.pano && d.key !== selectedImageKey) {
39160 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
39162 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
39168 updateDetections: function updateDetections(imageKey, url) {
39169 if (!_mlyViewer || _mlyFallback) return;
39170 if (!imageKey) return;
39172 if (!_mlyCache.image_detections.forImageKey[imageKey]) {
39173 loadData('image_detections', url).then(function () {
39174 showDetections(_mlyCache.image_detections.forImageKey[imageKey] || []);
39177 showDetections(_mlyCache.image_detections.forImageKey[imageKey]);
39180 function showDetections(detections) {
39181 detections.forEach(function (data) {
39182 var tag = makeTag(data);
39185 var tagComponent = _mlyViewer.getComponent('tag');
39187 tagComponent.add([tag]);
39192 function makeTag(data) {
39193 var valueParts = data.value.split('--');
39194 if (!valueParts.length) return;
39197 var color = 0xffffff;
39199 if (_mlyHighlightedDetection === data.key) {
39201 text = valueParts[1];
39203 if (text === 'flat' || text === 'discrete' || text === 'sign') {
39204 text = valueParts[2];
39207 text = text.replace(/-/g, ' ');
39208 text = text.charAt(0).toUpperCase() + text.slice(1);
39209 _mlyHighlightedDetection = null;
39212 if (data.shape.type === 'Polygon') {
39213 var polygonGeometry = new Mapillary.TagComponent.PolygonGeometry(data.shape.coordinates[0]);
39214 tag = new Mapillary.TagComponent.OutlineTag(data.key, polygonGeometry, {
39222 } else if (data.shape.type === 'Point') {
39223 var pointGeometry = new Mapillary.TagComponent.PointGeometry(data.shape.coordinates[0]);
39224 tag = new Mapillary.TagComponent.SpotTag(data.key, pointGeometry, {
39234 cache: function cache() {
39239 function validationIssue(attrs) {
39240 this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag')
39242 this.subtype = attrs.subtype; // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
39244 this.severity = attrs.severity; // required - 'warning' or 'error'
39246 this.message = attrs.message; // required - function returning localized string
39248 this.reference = attrs.reference; // optional - function(selection) to render reference information
39250 this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue
39252 this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue
39254 this.data = attrs.data; // optional - object containing extra data for the fixes
39256 this.dynamicFixes = attrs.dynamicFixes; // optional - function(context) returning fixes
39258 this.hash = attrs.hash; // optional - string to further differentiate the issue
39260 this.id = generateID.apply(this); // generated - see below
39262 this.autoFix = null; // generated - if autofix exists, will be set below
39263 // A unique, deterministic string hash.
39264 // Issues with identical id values are considered identical.
39266 function generateID() {
39267 var parts = [this.type];
39270 // subclasses can pass in their own differentiator
39271 parts.push(this.hash);
39274 if (this.subtype) {
39275 parts.push(this.subtype);
39276 } // include the entities this issue is for
39277 // (sort them so the id is deterministic)
39280 if (this.entityIds) {
39281 var entityKeys = this.entityIds.slice().sort();
39282 parts.push.apply(parts, entityKeys);
39285 return parts.join(':');
39288 this.extent = function (resolver) {
39290 return geoExtent(this.loc);
39293 if (this.entityIds && this.entityIds.length) {
39294 return this.entityIds.reduce(function (extent, entityId) {
39295 return extent.extend(resolver.entity(entityId).extent(resolver));
39302 this.fixes = function (context) {
39303 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
39306 if (issue.severity === 'warning') {
39307 // allow ignoring any issue that's not an error
39308 fixes.push(new validationIssueFix({
39309 title: _t.html('issues.fix.ignore_issue.title'),
39310 icon: 'iD-icon-close',
39311 onClick: function onClick() {
39312 context.validator().ignoreIssue(this.issue.id);
39317 fixes.forEach(function (fix) {
39318 // the id doesn't matter as long as it's unique to this issue/fix
39319 fix.id = fix.title; // add a reference to the issue for use in actions
39323 if (fix.autoArgs) {
39324 issue.autoFix = fix;
39330 function validationIssueFix(attrs) {
39331 this.title = attrs.title; // Required
39333 this.onClick = attrs.onClick; // Optional - the function to run to apply the fix
39335 this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
39337 this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
39339 this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting.
39341 this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
39343 this.issue = null; // Generated link - added by validationIssue
39346 var buildRuleChecks = function buildRuleChecks() {
39348 equals: function equals(_equals) {
39349 return function (tags) {
39350 return Object.keys(_equals).every(function (k) {
39351 return _equals[k] === tags[k];
39355 notEquals: function notEquals(_notEquals) {
39356 return function (tags) {
39357 return Object.keys(_notEquals).some(function (k) {
39358 return _notEquals[k] !== tags[k];
39362 absence: function absence(_absence) {
39363 return function (tags) {
39364 return Object.keys(tags).indexOf(_absence) === -1;
39367 presence: function presence(_presence) {
39368 return function (tags) {
39369 return Object.keys(tags).indexOf(_presence) > -1;
39372 greaterThan: function greaterThan(_greaterThan) {
39373 var key = Object.keys(_greaterThan)[0];
39374 var value = _greaterThan[key];
39375 return function (tags) {
39376 return tags[key] > value;
39379 greaterThanEqual: function greaterThanEqual(_greaterThanEqual) {
39380 var key = Object.keys(_greaterThanEqual)[0];
39381 var value = _greaterThanEqual[key];
39382 return function (tags) {
39383 return tags[key] >= value;
39386 lessThan: function lessThan(_lessThan) {
39387 var key = Object.keys(_lessThan)[0];
39388 var value = _lessThan[key];
39389 return function (tags) {
39390 return tags[key] < value;
39393 lessThanEqual: function lessThanEqual(_lessThanEqual) {
39394 var key = Object.keys(_lessThanEqual)[0];
39395 var value = _lessThanEqual[key];
39396 return function (tags) {
39397 return tags[key] <= value;
39400 positiveRegex: function positiveRegex(_positiveRegex) {
39401 var tagKey = Object.keys(_positiveRegex)[0];
39403 var expression = _positiveRegex[tagKey].join('|');
39405 var regex = new RegExp(expression);
39406 return function (tags) {
39407 return regex.test(tags[tagKey]);
39410 negativeRegex: function negativeRegex(_negativeRegex) {
39411 var tagKey = Object.keys(_negativeRegex)[0];
39413 var expression = _negativeRegex[tagKey].join('|');
39415 var regex = new RegExp(expression);
39416 return function (tags) {
39417 return !regex.test(tags[tagKey]);
39423 var buildLineKeys = function buildLineKeys() {
39439 var serviceMapRules = {
39440 init: function init() {
39441 this._ruleChecks = buildRuleChecks();
39442 this._validationRules = [];
39443 this._areaKeys = osmAreaKeys;
39444 this._lineKeys = buildLineKeys();
39446 // list of rules only relevant to tag checks...
39447 filterRuleChecks: function filterRuleChecks(selector) {
39448 var _ruleChecks = this._ruleChecks;
39449 return Object.keys(selector).reduce(function (rules, key) {
39450 if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
39451 rules.push(_ruleChecks[key](selector[key]));
39457 // builds tagMap from mapcss-parse selector object...
39458 buildTagMap: function buildTagMap(selector) {
39459 var getRegexValues = function getRegexValues(regexes) {
39460 return regexes.map(function (regex) {
39461 return regex.replace(/\$|\^/g, '');
39465 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
39467 var isRegex = /regex/gi.test(key);
39468 var isEqual = /equals/gi.test(key);
39470 if (isRegex || isEqual) {
39471 Object.keys(selector[key]).forEach(function (selectorKey) {
39472 values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
39474 if (expectedTags.hasOwnProperty(selectorKey)) {
39475 values = values.concat(expectedTags[selectorKey]);
39478 expectedTags[selectorKey] = values;
39480 } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
39481 var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
39482 values = [selector[key][tagKey]];
39484 if (expectedTags.hasOwnProperty(tagKey)) {
39485 values = values.concat(expectedTags[tagKey]);
39488 expectedTags[tagKey] = values;
39491 return expectedTags;
39495 // inspired by osmWay#isArea()
39496 inferGeometry: function inferGeometry(tagMap) {
39497 var _lineKeys = this._lineKeys;
39498 var _areaKeys = this._areaKeys;
39500 var keyValueDoesNotImplyArea = function keyValueDoesNotImplyArea(key) {
39501 return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
39504 var keyValueImpliesLine = function keyValueImpliesLine(key) {
39505 return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
39508 if (tagMap.hasOwnProperty('area')) {
39509 if (tagMap.area.indexOf('yes') > -1) {
39513 if (tagMap.area.indexOf('no') > -1) {
39518 for (var key in tagMap) {
39519 if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
39523 if (key in _lineKeys && keyValueImpliesLine(key)) {
39530 // adds from mapcss-parse selector check...
39531 addRule: function addRule(selector) {
39533 // checks relevant to mapcss-selector
39534 checks: this.filterRuleChecks(selector),
39535 // true if all conditions for a tag error are true..
39536 matches: function matches(entity) {
39537 return this.checks.every(function (check) {
39538 return check(entity.tags);
39541 // borrowed from Way#isArea()
39542 inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
39543 geometryMatches: function geometryMatches(entity, graph) {
39544 if (entity.type === 'node' || entity.type === 'relation') {
39545 return selector.geometry === entity.type;
39546 } else if (entity.type === 'way') {
39547 return this.inferredGeometry === entity.geometry(graph);
39550 // when geometries match and tag matches are present, return a warning...
39551 findIssues: function findIssues(entity, graph, issues) {
39552 if (this.geometryMatches(entity, graph) && this.matches(entity)) {
39553 var severity = Object.keys(selector).indexOf('error') > -1 ? 'error' : 'warning';
39554 var _message = selector[severity];
39555 issues.push(new validationIssue({
39557 severity: severity,
39558 message: function message() {
39561 entityIds: [entity.id]
39567 this._validationRules.push(rule);
39569 clearRules: function clearRules() {
39570 this._validationRules = [];
39572 // returns validationRules...
39573 validationRules: function validationRules() {
39574 return this._validationRules;
39576 // returns ruleChecks
39577 ruleChecks: function ruleChecks() {
39578 return this._ruleChecks;
39582 var apibase$1 = 'https://nominatim.openstreetmap.org/';
39583 var _inflight = {};
39585 var _nominatimCache;
39587 var serviceNominatim = {
39588 init: function init() {
39590 _nominatimCache = new RBush();
39592 reset: function reset() {
39593 Object.values(_inflight).forEach(function (controller) {
39594 controller.abort();
39597 _nominatimCache = new RBush();
39599 countryCode: function countryCode(location, callback) {
39600 this.reverse(location, function (err, result) {
39602 return callback(err);
39603 } else if (result.address) {
39604 return callback(null, result.address.country_code);
39606 return callback('Unable to geocode', null);
39610 reverse: function reverse(loc, callback) {
39611 var cached = _nominatimCache.search({
39618 if (cached.length > 0) {
39619 if (callback) callback(null, cached[0].data);
39630 var url = apibase$1 + 'reverse?' + utilQsString(params);
39631 if (_inflight[url]) return;
39632 var controller = new AbortController();
39633 _inflight[url] = controller;
39635 signal: controller.signal
39636 }).then(function (result) {
39637 delete _inflight[url];
39639 if (result && result.error) {
39640 throw new Error(result.error);
39643 var extent = geoExtent(loc).padByMeters(200);
39645 _nominatimCache.insert(Object.assign(extent.bbox(), {
39649 if (callback) callback(null, result);
39650 })["catch"](function (err) {
39651 delete _inflight[url];
39652 if (err.name === 'AbortError') return;
39653 if (callback) callback(err.message);
39656 search: function search(val, callback) {
39657 var searchVal = encodeURIComponent(val);
39658 var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
39659 if (_inflight[url]) return;
39660 var controller = new AbortController();
39661 _inflight[url] = controller;
39663 signal: controller.signal
39664 }).then(function (result) {
39665 delete _inflight[url];
39667 if (result && result.error) {
39668 throw new Error(result.error);
39671 if (callback) callback(null, result);
39672 })["catch"](function (err) {
39673 delete _inflight[url];
39674 if (err.name === 'AbortError') return;
39675 if (callback) callback(err.message);
39680 var apibase$2 = 'https://openstreetcam.org';
39681 var maxResults$1 = 1000;
39682 var tileZoom$1 = 14;
39683 var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
39684 var dispatch$5 = dispatch('loadedImages');
39685 var imgZoom = d3_zoom().extent([[0, 0], [320, 240]]).translateExtent([[0, 0], [320, 240]]).scaleExtent([1, 15]);
39689 var _oscSelectedImage;
39691 var _loadViewerPromise$1;
39693 function abortRequest$4(controller) {
39694 controller.abort();
39697 function maxPageAtZoom$1(z) {
39698 if (z < 15) return 2;
39699 if (z === 15) return 5;
39700 if (z === 16) return 10;
39701 if (z === 17) return 20;
39702 if (z === 18) return 40;
39703 if (z > 18) return 80;
39706 function loadTiles$1(which, url, projection) {
39707 var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
39708 var tiles = tiler$4.getTiles(projection); // abort inflight requests that are no longer needed
39710 var cache = _oscCache[which];
39711 Object.keys(cache.inflight).forEach(function (k) {
39712 var wanted = tiles.find(function (tile) {
39713 return k.indexOf(tile.id + ',') === 0;
39717 abortRequest$4(cache.inflight[k]);
39718 delete cache.inflight[k];
39721 tiles.forEach(function (tile) {
39722 loadNextTilePage$1(which, currZoom, url, tile);
39726 function loadNextTilePage$1(which, currZoom, url, tile) {
39727 var cache = _oscCache[which];
39728 var bbox = tile.extent.bbox();
39729 var maxPages = maxPageAtZoom$1(currZoom);
39730 var nextPage = cache.nextPage[tile.id] || 1;
39731 var params = utilQsString({
39734 // client_id: clientId,
39735 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
39736 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
39738 if (nextPage > maxPages) return;
39739 var id = tile.id + ',' + String(nextPage);
39740 if (cache.loaded[id] || cache.inflight[id]) return;
39741 var controller = new AbortController();
39742 cache.inflight[id] = controller;
39745 signal: controller.signal,
39748 'Content-Type': 'application/x-www-form-urlencoded'
39751 d3_json(url, options).then(function (data) {
39752 cache.loaded[id] = true;
39753 delete cache.inflight[id];
39755 if (!data || !data.currentPageItems || !data.currentPageItems.length) {
39756 throw new Error('No Data');
39759 var features = data.currentPageItems.map(function (item) {
39760 var loc = [+item.lng, +item.lat];
39763 if (which === 'images') {
39768 captured_at: item.shot_date || item.date_added,
39769 captured_by: item.username,
39770 imagePath: item.lth_name,
39771 sequence_id: item.sequence_id,
39772 sequence_index: +item.sequence_index
39773 }; // cache sequence info
39775 var seq = _oscCache.sequences[d.sequence_id];
39782 _oscCache.sequences[d.sequence_id] = seq;
39785 seq.images[d.sequence_index] = d;
39786 _oscCache.images.forImageKey[d.key] = d; // cache imageKey -> image
39797 cache.rtree.load(features);
39799 if (data.currentPageItems.length === maxResults$1) {
39800 // more pages to load
39801 cache.nextPage[tile.id] = nextPage + 1;
39802 loadNextTilePage$1(which, currZoom, url, tile);
39804 cache.nextPage[tile.id] = Infinity; // no more pages to load
39807 if (which === 'images') {
39808 dispatch$5.call('loadedImages');
39810 })["catch"](function () {
39811 cache.loaded[id] = true;
39812 delete cache.inflight[id];
39814 } // partition viewport into higher zoom tiles
39817 function partitionViewport$1(projection) {
39818 var z = geoScaleToZoom(projection.scale());
39819 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
39821 var tiler = utilTiler().zoomExtent([z2, z2]);
39822 return tiler.getTiles(projection).map(function (tile) {
39823 return tile.extent;
39825 } // no more than `limit` results per partition.
39828 function searchLimited$1(limit, projection, rtree) {
39829 limit = limit || 5;
39830 return partitionViewport$1(projection).reduce(function (result, extent) {
39831 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
39834 return found.length ? result.concat(found) : result;
39838 var serviceOpenstreetcam = {
39839 init: function init() {
39844 this.event = utilRebind(this, dispatch$5, 'on');
39846 reset: function reset() {
39848 Object.values(_oscCache.images.inflight).forEach(abortRequest$4);
39856 rtree: new RBush(),
39861 _oscSelectedImage = null;
39863 images: function images(projection) {
39865 return searchLimited$1(limit, projection, _oscCache.images.rtree);
39867 sequences: function sequences(projection) {
39868 var viewport = projection.clipExtent();
39869 var min = [viewport[0][0], viewport[1][1]];
39870 var max = [viewport[1][0], viewport[0][1]];
39871 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
39872 var sequenceKeys = {}; // all sequences for images in viewport
39874 _oscCache.images.rtree.search(bbox).forEach(function (d) {
39875 sequenceKeys[d.data.sequence_id] = true;
39876 }); // make linestrings from those sequences
39879 var lineStrings = [];
39880 Object.keys(sequenceKeys).forEach(function (sequenceKey) {
39881 var seq = _oscCache.sequences[sequenceKey];
39882 var images = seq && seq.images;
39886 type: 'LineString',
39887 coordinates: images.map(function (d) {
39889 }).filter(Boolean),
39891 captured_at: images[0] ? images[0].captured_at : null,
39892 captured_by: images[0] ? images[0].captured_by : null,
39898 return lineStrings;
39900 cachedImage: function cachedImage(imageKey) {
39901 return _oscCache.images.forImageKey[imageKey];
39903 loadImages: function loadImages(projection) {
39904 var url = apibase$2 + '/1.0/list/nearby-photos/';
39905 loadTiles$1('images', url, projection);
39907 ensureViewerLoaded: function ensureViewerLoaded(context) {
39908 if (_loadViewerPromise$1) return _loadViewerPromise$1; // add osc-wrapper
39910 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper').data([0]);
39912 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper osc-wrapper').classed('hide', true).call(imgZoom.on('zoom', zoomPan)).on('dblclick.zoom', null);
39913 wrapEnter.append('div').attr('class', 'photo-attribution fillD');
39914 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
39915 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
39916 controlsEnter.append('button').on('click.rotate-ccw', rotate(-90)).html('⤿');
39917 controlsEnter.append('button').on('click.rotate-cw', rotate(90)).html('⤾');
39918 controlsEnter.append('button').on('click.forward', step(1)).html('►');
39919 wrapEnter.append('div').attr('class', 'osc-image-wrap'); // Register viewer resize handler
39921 context.ui().photoviewer.on('resize.openstreetcam', function (dimensions) {
39922 imgZoom = d3_zoom().extent([[0, 0], dimensions]).translateExtent([[0, 0], dimensions]).scaleExtent([1, 15]).on('zoom', zoomPan);
39925 function zoomPan(d3_event) {
39926 var t = d3_event.transform;
39927 context.container().select('.photoviewer .osc-image-wrap').call(utilSetTransform, t.x, t.y, t.k);
39930 function rotate(deg) {
39931 return function () {
39932 if (!_oscSelectedImage) return;
39933 var sequenceKey = _oscSelectedImage.sequence_id;
39934 var sequence = _oscCache.sequences[sequenceKey];
39935 if (!sequence) return;
39936 var r = sequence.rotation || 0;
39938 if (r > 180) r -= 360;
39939 if (r < -180) r += 360;
39940 sequence.rotation = r;
39941 var wrap = context.container().select('.photoviewer .osc-wrapper');
39942 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
39943 wrap.selectAll('.osc-image').transition().duration(100).style('transform', 'rotate(' + r + 'deg)');
39947 function step(stepBy) {
39948 return function () {
39949 if (!_oscSelectedImage) return;
39950 var sequenceKey = _oscSelectedImage.sequence_id;
39951 var sequence = _oscCache.sequences[sequenceKey];
39952 if (!sequence) return;
39953 var nextIndex = _oscSelectedImage.sequence_index + stepBy;
39954 var nextImage = sequence.images[nextIndex];
39955 if (!nextImage) return;
39956 context.map().centerEase(nextImage.loc);
39957 that.selectImage(context, nextImage.key);
39959 } // don't need any async loading so resolve immediately
39962 _loadViewerPromise$1 = Promise.resolve();
39963 return _loadViewerPromise$1;
39965 showViewer: function showViewer(context) {
39966 var viewer = context.container().select('.photoviewer').classed('hide', false);
39967 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
39970 viewer.selectAll('.photo-wrapper:not(.osc-wrapper)').classed('hide', true);
39971 viewer.selectAll('.photo-wrapper.osc-wrapper').classed('hide', false);
39976 hideViewer: function hideViewer(context) {
39977 _oscSelectedImage = null;
39978 this.updateUrlImage(null);
39979 var viewer = context.container().select('.photoviewer');
39980 if (!viewer.empty()) viewer.datum(null);
39981 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
39982 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
39983 return this.setStyles(context, null, true);
39985 selectImage: function selectImage(context, imageKey) {
39986 var d = this.cachedImage(imageKey);
39987 _oscSelectedImage = d;
39988 this.updateUrlImage(imageKey);
39989 var viewer = context.container().select('.photoviewer');
39990 if (!viewer.empty()) viewer.datum(d);
39991 this.setStyles(context, null, true);
39992 context.container().selectAll('.icon-sign').classed('currentView', false);
39993 if (!d) return this;
39994 var wrap = context.container().select('.photoviewer .osc-wrapper');
39995 var imageWrap = wrap.selectAll('.osc-image-wrap');
39996 var attribution = wrap.selectAll('.photo-attribution').html('');
39997 wrap.transition().duration(100).call(imgZoom.transform, identity$2);
39998 imageWrap.selectAll('.osc-image').remove();
40001 var sequence = _oscCache.sequences[d.sequence_id];
40002 var r = sequence && sequence.rotation || 0;
40003 imageWrap.append('img').attr('class', 'osc-image').attr('src', apibase$2 + '/' + d.imagePath).style('transform', 'rotate(' + r + 'deg)');
40005 if (d.captured_by) {
40006 attribution.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by)).html('@' + d.captured_by);
40007 attribution.append('span').html('|');
40010 if (d.captured_at) {
40011 attribution.append('span').attr('class', 'captured_at').html(localeDateString(d.captured_at));
40012 attribution.append('span').html('|');
40015 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');
40020 function localeDateString(s) {
40021 if (!s) return null;
40027 var d = new Date(s);
40028 if (isNaN(d.getTime())) return null;
40029 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
40032 getSelectedImage: function getSelectedImage() {
40033 return _oscSelectedImage;
40035 getSequenceKeyForImage: function getSequenceKeyForImage(d) {
40036 return d && d.sequence_id;
40038 // Updates the currently highlighted sequence and selected bubble.
40039 // Reset is only necessary when interacting with the viewport because
40040 // this implicitly changes the currently selected bubble/sequence
40041 setStyles: function setStyles(context, hovered, reset) {
40043 // reset all layers
40044 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
40045 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
40048 var hoveredImageKey = hovered && hovered.key;
40049 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
40050 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
40051 var hoveredImageKeys = hoveredSequence && hoveredSequence.images.map(function (d) {
40054 var viewer = context.container().select('.photoviewer');
40055 var selected = viewer.empty() ? undefined : viewer.datum();
40056 var selectedImageKey = selected && selected.key;
40057 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
40058 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
40059 var selectedImageKeys = selectedSequence && selectedSequence.images.map(function (d) {
40061 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
40063 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
40064 context.container().selectAll('.layer-openstreetcam .viewfield-group').classed('highlighted', function (d) {
40065 return highlightedImageKeys.indexOf(d.key) !== -1;
40066 }).classed('hovered', function (d) {
40067 return d.key === hoveredImageKey;
40068 }).classed('currentView', function (d) {
40069 return d.key === selectedImageKey;
40071 context.container().selectAll('.layer-openstreetcam .sequence').classed('highlighted', function (d) {
40072 return d.properties.key === hoveredSequenceKey;
40073 }).classed('currentView', function (d) {
40074 return d.properties.key === selectedSequenceKey;
40075 }); // update viewfields if needed
40077 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
40079 function viewfieldPath() {
40080 var d = this.parentNode.__data__;
40082 if (d.pano && d.key !== selectedImageKey) {
40083 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
40085 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
40091 updateUrlImage: function updateUrlImage(imageKey) {
40092 if (!window.mocha) {
40093 var hash = utilStringQs(window.location.hash);
40096 hash.photo = 'openstreetcam/' + imageKey;
40101 window.location.replace('#' + utilQsString(hash, true));
40104 cache: function cache() {
40109 var FORCED$f = fails(function () {
40110 return new Date(NaN).toJSON() !== null
40111 || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1;
40114 // `Date.prototype.toJSON` method
40115 // https://tc39.github.io/ecma262/#sec-date.prototype.tojson
40116 _export({ target: 'Date', proto: true, forced: FORCED$f }, {
40117 // eslint-disable-next-line no-unused-vars
40118 toJSON: function toJSON(key) {
40119 var O = toObject(this);
40120 var pv = toPrimitive(O);
40121 return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString();
40125 // `URL.prototype.toJSON` method
40126 // https://url.spec.whatwg.org/#dom-url-tojson
40127 _export({ target: 'URL', proto: true, enumerable: true }, {
40128 toJSON: function toJSON() {
40129 return URL.prototype.toString.call(this);
40134 * Checks if `value` is the
40135 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
40136 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
40142 * @param {*} value The value to check.
40143 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
40149 * _.isObject([1, 2, 3]);
40152 * _.isObject(_.noop);
40155 * _.isObject(null);
40158 function isObject$1(value) {
40159 var type = _typeof(value);
40161 return value != null && (type == 'object' || type == 'function');
40164 /** Detect free variable `global` from Node.js. */
40165 var freeGlobal = (typeof global === "undefined" ? "undefined" : _typeof(global)) == 'object' && global && global.Object === Object && global;
40167 /** Detect free variable `self`. */
40169 var freeSelf = (typeof self === "undefined" ? "undefined" : _typeof(self)) == 'object' && self && self.Object === Object && self;
40170 /** Used as a reference to the global object. */
40172 var root$1 = freeGlobal || freeSelf || Function('return this')();
40175 * Gets the timestamp of the number of milliseconds that have elapsed since
40176 * the Unix epoch (1 January 1970 00:00:00 UTC).
40182 * @returns {number} Returns the timestamp.
40185 * _.defer(function(stamp) {
40186 * console.log(_.now() - stamp);
40188 * // => Logs the number of milliseconds it took for the deferred invocation.
40191 var now$1 = function now() {
40192 return root$1.Date.now();
40195 /** Built-in value references. */
40197 var _Symbol = root$1.Symbol;
40199 /** Used for built-in method references. */
40201 var objectProto = Object.prototype;
40202 /** Used to check objects for own properties. */
40204 var hasOwnProperty$1 = objectProto.hasOwnProperty;
40206 * Used to resolve the
40207 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
40211 var nativeObjectToString = objectProto.toString;
40212 /** Built-in value references. */
40214 var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
40216 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
40219 * @param {*} value The value to query.
40220 * @returns {string} Returns the raw `toStringTag`.
40223 function getRawTag(value) {
40224 var isOwn = hasOwnProperty$1.call(value, symToStringTag),
40225 tag = value[symToStringTag];
40228 value[symToStringTag] = undefined;
40229 var unmasked = true;
40232 var result = nativeObjectToString.call(value);
40236 value[symToStringTag] = tag;
40238 delete value[symToStringTag];
40245 /** Used for built-in method references. */
40246 var objectProto$1 = Object.prototype;
40248 * Used to resolve the
40249 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
40253 var nativeObjectToString$1 = objectProto$1.toString;
40255 * Converts `value` to a string using `Object.prototype.toString`.
40258 * @param {*} value The value to convert.
40259 * @returns {string} Returns the converted string.
40262 function objectToString$1(value) {
40263 return nativeObjectToString$1.call(value);
40266 /** `Object#toString` result references. */
40268 var nullTag = '[object Null]',
40269 undefinedTag = '[object Undefined]';
40270 /** Built-in value references. */
40272 var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
40274 * The base implementation of `getTag` without fallbacks for buggy environments.
40277 * @param {*} value The value to query.
40278 * @returns {string} Returns the `toStringTag`.
40281 function baseGetTag(value) {
40282 if (value == null) {
40283 return value === undefined ? undefinedTag : nullTag;
40286 return symToStringTag$1 && symToStringTag$1 in Object(value) ? getRawTag(value) : objectToString$1(value);
40290 * Checks if `value` is object-like. A value is object-like if it's not `null`
40291 * and has a `typeof` result of "object".
40297 * @param {*} value The value to check.
40298 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
40301 * _.isObjectLike({});
40304 * _.isObjectLike([1, 2, 3]);
40307 * _.isObjectLike(_.noop);
40310 * _.isObjectLike(null);
40313 function isObjectLike(value) {
40314 return value != null && _typeof(value) == 'object';
40317 /** `Object#toString` result references. */
40319 var symbolTag = '[object Symbol]';
40321 * Checks if `value` is classified as a `Symbol` primitive or object.
40327 * @param {*} value The value to check.
40328 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
40331 * _.isSymbol(Symbol.iterator);
40334 * _.isSymbol('abc');
40338 function isSymbol$1(value) {
40339 return _typeof(value) == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
40342 /** Used as references for various `Number` constants. */
40345 /** Used to match leading and trailing whitespace. */
40347 var reTrim = /^\s+|\s+$/g;
40348 /** Used to detect bad signed hexadecimal string values. */
40350 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
40351 /** Used to detect binary string values. */
40353 var reIsBinary = /^0b[01]+$/i;
40354 /** Used to detect octal string values. */
40356 var reIsOctal = /^0o[0-7]+$/i;
40357 /** Built-in method references without a dependency on `root`. */
40359 var freeParseInt = parseInt;
40361 * Converts `value` to a number.
40367 * @param {*} value The value to process.
40368 * @returns {number} Returns the number.
40374 * _.toNumber(Number.MIN_VALUE);
40377 * _.toNumber(Infinity);
40380 * _.toNumber('3.2');
40384 function toNumber$1(value) {
40385 if (typeof value == 'number') {
40389 if (isSymbol$1(value)) {
40393 if (isObject$1(value)) {
40394 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
40395 value = isObject$1(other) ? other + '' : other;
40398 if (typeof value != 'string') {
40399 return value === 0 ? value : +value;
40402 value = value.replace(reTrim, '');
40403 var isBinary = reIsBinary.test(value);
40404 return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
40407 /** Error message constants. */
40409 var FUNC_ERROR_TEXT = 'Expected a function';
40410 /* Built-in method references for those with the same name as other `lodash` methods. */
40412 var nativeMax = Math.max,
40413 nativeMin = Math.min;
40415 * Creates a debounced function that delays invoking `func` until after `wait`
40416 * milliseconds have elapsed since the last time the debounced function was
40417 * invoked. The debounced function comes with a `cancel` method to cancel
40418 * delayed `func` invocations and a `flush` method to immediately invoke them.
40419 * Provide `options` to indicate whether `func` should be invoked on the
40420 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
40421 * with the last arguments provided to the debounced function. Subsequent
40422 * calls to the debounced function return the result of the last `func`
40425 * **Note:** If `leading` and `trailing` options are `true`, `func` is
40426 * invoked on the trailing edge of the timeout only if the debounced function
40427 * is invoked more than once during the `wait` timeout.
40429 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
40430 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
40432 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
40433 * for details over the differences between `_.debounce` and `_.throttle`.
40438 * @category Function
40439 * @param {Function} func The function to debounce.
40440 * @param {number} [wait=0] The number of milliseconds to delay.
40441 * @param {Object} [options={}] The options object.
40442 * @param {boolean} [options.leading=false]
40443 * Specify invoking on the leading edge of the timeout.
40444 * @param {number} [options.maxWait]
40445 * The maximum time `func` is allowed to be delayed before it's invoked.
40446 * @param {boolean} [options.trailing=true]
40447 * Specify invoking on the trailing edge of the timeout.
40448 * @returns {Function} Returns the new debounced function.
40451 * // Avoid costly calculations while the window size is in flux.
40452 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
40454 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
40455 * jQuery(element).on('click', _.debounce(sendMail, 300, {
40457 * 'trailing': false
40460 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
40461 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
40462 * var source = new EventSource('/stream');
40463 * jQuery(source).on('message', debounced);
40465 * // Cancel the trailing debounced invocation.
40466 * jQuery(window).on('popstate', debounced.cancel);
40469 function debounce(func, wait, options) {
40476 lastInvokeTime = 0,
40481 if (typeof func != 'function') {
40482 throw new TypeError(FUNC_ERROR_TEXT);
40485 wait = toNumber$1(wait) || 0;
40487 if (isObject$1(options)) {
40488 leading = !!options.leading;
40489 maxing = 'maxWait' in options;
40490 maxWait = maxing ? nativeMax(toNumber$1(options.maxWait) || 0, wait) : maxWait;
40491 trailing = 'trailing' in options ? !!options.trailing : trailing;
40494 function invokeFunc(time) {
40495 var args = lastArgs,
40496 thisArg = lastThis;
40497 lastArgs = lastThis = undefined;
40498 lastInvokeTime = time;
40499 result = func.apply(thisArg, args);
40503 function leadingEdge(time) {
40504 // Reset any `maxWait` timer.
40505 lastInvokeTime = time; // Start the timer for the trailing edge.
40507 timerId = setTimeout(timerExpired, wait); // Invoke the leading edge.
40509 return leading ? invokeFunc(time) : result;
40512 function remainingWait(time) {
40513 var timeSinceLastCall = time - lastCallTime,
40514 timeSinceLastInvoke = time - lastInvokeTime,
40515 timeWaiting = wait - timeSinceLastCall;
40516 return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
40519 function shouldInvoke(time) {
40520 var timeSinceLastCall = time - lastCallTime,
40521 timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the
40522 // trailing edge, the system time has gone backwards and we're treating
40523 // it as the trailing edge, or we've hit the `maxWait` limit.
40525 return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
40528 function timerExpired() {
40529 var time = now$1();
40531 if (shouldInvoke(time)) {
40532 return trailingEdge(time);
40533 } // Restart the timer.
40536 timerId = setTimeout(timerExpired, remainingWait(time));
40539 function trailingEdge(time) {
40540 timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been
40541 // debounced at least once.
40543 if (trailing && lastArgs) {
40544 return invokeFunc(time);
40547 lastArgs = lastThis = undefined;
40551 function cancel() {
40552 if (timerId !== undefined) {
40553 clearTimeout(timerId);
40556 lastInvokeTime = 0;
40557 lastArgs = lastCallTime = lastThis = timerId = undefined;
40561 return timerId === undefined ? result : trailingEdge(now$1());
40564 function debounced() {
40565 var time = now$1(),
40566 isInvoking = shouldInvoke(time);
40567 lastArgs = arguments;
40569 lastCallTime = time;
40572 if (timerId === undefined) {
40573 return leadingEdge(lastCallTime);
40577 // Handle invocations in a tight loop.
40578 clearTimeout(timerId);
40579 timerId = setTimeout(timerExpired, wait);
40580 return invokeFunc(lastCallTime);
40584 if (timerId === undefined) {
40585 timerId = setTimeout(timerExpired, wait);
40591 debounced.cancel = cancel;
40592 debounced.flush = flush;
40596 /** Error message constants. */
40598 var FUNC_ERROR_TEXT$1 = 'Expected a function';
40600 * Creates a throttled function that only invokes `func` at most once per
40601 * every `wait` milliseconds. The throttled function comes with a `cancel`
40602 * method to cancel delayed `func` invocations and a `flush` method to
40603 * immediately invoke them. Provide `options` to indicate whether `func`
40604 * should be invoked on the leading and/or trailing edge of the `wait`
40605 * timeout. The `func` is invoked with the last arguments provided to the
40606 * throttled function. Subsequent calls to the throttled function return the
40607 * result of the last `func` invocation.
40609 * **Note:** If `leading` and `trailing` options are `true`, `func` is
40610 * invoked on the trailing edge of the timeout only if the throttled function
40611 * is invoked more than once during the `wait` timeout.
40613 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
40614 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
40616 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
40617 * for details over the differences between `_.throttle` and `_.debounce`.
40622 * @category Function
40623 * @param {Function} func The function to throttle.
40624 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
40625 * @param {Object} [options={}] The options object.
40626 * @param {boolean} [options.leading=true]
40627 * Specify invoking on the leading edge of the timeout.
40628 * @param {boolean} [options.trailing=true]
40629 * Specify invoking on the trailing edge of the timeout.
40630 * @returns {Function} Returns the new throttled function.
40633 * // Avoid excessively updating the position while scrolling.
40634 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
40636 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
40637 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
40638 * jQuery(element).on('click', throttled);
40640 * // Cancel the trailing throttled invocation.
40641 * jQuery(window).on('popstate', throttled.cancel);
40644 function throttle(func, wait, options) {
40645 var leading = true,
40648 if (typeof func != 'function') {
40649 throw new TypeError(FUNC_ERROR_TEXT$1);
40652 if (isObject$1(options)) {
40653 leading = 'leading' in options ? !!options.leading : leading;
40654 trailing = 'trailing' in options ? !!options.trailing : trailing;
40657 return debounce(func, wait, {
40658 'leading': leading,
40660 'trailing': trailing
40664 var hashes = createCommonjsModule(function (module, exports) {
40666 * jshashes - https://github.com/h2non/jshashes
40667 * Released under the "New BSD" license
40669 * Algorithms specification:
40671 * MD5 - http://www.ietf.org/rfc/rfc1321.txt
40672 * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
40673 * SHA1 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40674 * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40675 * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
40676 * HMAC - http://www.ietf.org/rfc/rfc2104.txt
40681 function utf8Encode(str) {
40688 if (str && str.length) {
40691 while ((i += 1) < l) {
40692 /* Decode utf-16 surrogate pairs */
40693 x = str.charCodeAt(i);
40694 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
40696 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
40697 x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
40700 /* Encode output as utf-8 */
40704 output += String.fromCharCode(x);
40705 } else if (x <= 0x7FF) {
40706 output += String.fromCharCode(0xC0 | x >>> 6 & 0x1F, 0x80 | x & 0x3F);
40707 } else if (x <= 0xFFFF) {
40708 output += String.fromCharCode(0xE0 | x >>> 12 & 0x0F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
40709 } else if (x <= 0x1FFFFF) {
40710 output += String.fromCharCode(0xF0 | x >>> 18 & 0x07, 0x80 | x >>> 12 & 0x3F, 0x80 | x >>> 6 & 0x3F, 0x80 | x & 0x3F);
40718 function utf8Decode(str) {
40726 i = ac = c1 = c2 = c3 = 0;
40728 if (str && str.length) {
40733 c1 = str.charCodeAt(i);
40737 arr[ac] = String.fromCharCode(c1);
40739 } else if (c1 > 191 && c1 < 224) {
40740 c2 = str.charCodeAt(i + 1);
40741 arr[ac] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
40744 c2 = str.charCodeAt(i + 1);
40745 c3 = str.charCodeAt(i + 2);
40746 arr[ac] = String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
40752 return arr.join('');
40755 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
40756 * to work around bugs in some JS interpreters.
40760 function safe_add(x, y) {
40761 var lsw = (x & 0xFFFF) + (y & 0xFFFF),
40762 msw = (x >> 16) + (y >> 16) + (lsw >> 16);
40763 return msw << 16 | lsw & 0xFFFF;
40766 * Bitwise rotate a 32-bit number to the left.
40770 function bit_rol(num, cnt) {
40771 return num << cnt | num >>> 32 - cnt;
40774 * Convert a raw string to a hex string
40778 function rstr2hex(input, hexcase) {
40779 var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
40785 for (; i < l; i += 1) {
40786 x = input.charCodeAt(i);
40787 output += hex_tab.charAt(x >>> 4 & 0x0F) + hex_tab.charAt(x & 0x0F);
40793 * Convert an array of big-endian words to a string
40797 function binb2rstr(input) {
40799 l = input.length * 32,
40802 for (i = 0; i < l; i += 8) {
40803 output += String.fromCharCode(input[i >> 5] >>> 24 - i % 32 & 0xFF);
40809 * Convert an array of little-endian words to a string
40813 function binl2rstr(input) {
40815 l = input.length * 32,
40818 for (i = 0; i < l; i += 8) {
40819 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
40825 * Convert a raw string to an array of little-endian words
40826 * Characters >255 have their high-byte silently ignored.
40830 function rstr2binl(input) {
40832 l = input.length * 8,
40833 output = Array(input.length >> 2),
40834 lo = output.length;
40836 for (i = 0; i < lo; i += 1) {
40840 for (i = 0; i < l; i += 8) {
40841 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << i % 32;
40847 * Convert a raw string to an array of big-endian words
40848 * Characters >255 have their high-byte silently ignored.
40852 function rstr2binb(input) {
40854 l = input.length * 8,
40855 output = Array(input.length >> 2),
40856 lo = output.length;
40858 for (i = 0; i < lo; i += 1) {
40862 for (i = 0; i < l; i += 8) {
40863 output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << 24 - i % 32;
40869 * Convert a raw string to an arbitrary string encoding
40873 function rstr2any(input, encoding) {
40874 var divisor = encoding.length,
40875 remainders = Array(),
40884 /* Convert to an array of 16-bit big-endian values, forming the dividend */
40886 dividend = Array(Math.ceil(input.length / 2));
40887 ld = dividend.length;
40889 for (i = 0; i < ld; i += 1) {
40890 dividend[i] = input.charCodeAt(i * 2) << 8 | input.charCodeAt(i * 2 + 1);
40893 * Repeatedly perform a long division. The binary array forms the dividend,
40894 * the length of the encoding is the divisor. Once computed, the quotient
40895 * forms the dividend for the next step. We stop when the dividend is zerHashes.
40896 * All remainders are stored for later use.
40900 while (dividend.length > 0) {
40901 quotient = Array();
40904 for (i = 0; i < dividend.length; i += 1) {
40905 x = (x << 16) + dividend[i];
40906 q = Math.floor(x / divisor);
40909 if (quotient.length > 0 || q > 0) {
40910 quotient[quotient.length] = q;
40914 remainders[remainders.length] = x;
40915 dividend = quotient;
40917 /* Convert the remainders to the output string */
40922 for (i = remainders.length - 1; i >= 0; i--) {
40923 output += encoding.charAt(remainders[i]);
40925 /* Append leading zero equivalents */
40928 full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
40930 for (i = output.length; i < full_length; i += 1) {
40931 output = encoding[0] + output;
40937 * Convert a raw string to a base-64 string
40941 function rstr2b64(input, b64pad) {
40942 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
40944 len = input.length,
40948 b64pad = b64pad || '=';
40950 for (i = 0; i < len; i += 3) {
40951 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
40953 for (j = 0; j < 4; j += 1) {
40954 if (i * 8 + j * 6 > input.length * 8) {
40957 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
40967 * @property {String} version
40977 Base64: function Base64() {
40978 // private properties
40979 var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
40981 // URL encoding support @todo
40982 utf8 = true; // by default enable UTF-8 support encoding
40983 // public method for encoding
40985 this.encode = function (input) {
40990 len = input.length;
40992 input = utf8 ? utf8Encode(input) : input;
40994 for (i = 0; i < len; i += 3) {
40995 triplet = input.charCodeAt(i) << 16 | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
40997 for (j = 0; j < 4; j += 1) {
40998 if (i * 8 + j * 6 > len * 8) {
41001 output += tab.charAt(triplet >>> 6 * (3 - j) & 0x3F);
41007 }; // public method for decoding
41010 this.decode = function (input) {
41011 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
41030 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
41034 // unpack four hexets into three octets using index points in b64
41035 h1 = tab.indexOf(input.charAt(i += 1));
41036 h2 = tab.indexOf(input.charAt(i += 1));
41037 h3 = tab.indexOf(input.charAt(i += 1));
41038 h4 = tab.indexOf(input.charAt(i += 1));
41039 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
41040 o1 = bits >> 16 & 0xff;
41041 o2 = bits >> 8 & 0xff;
41046 arr[ac] = String.fromCharCode(o1);
41047 } else if (h4 === 64) {
41048 arr[ac] = String.fromCharCode(o1, o2);
41050 arr[ac] = String.fromCharCode(o1, o2, o3);
41052 } while (i < input.length);
41054 dec = arr.join('');
41055 dec = utf8 ? utf8Decode(dec) : dec;
41057 }; // set custom pad string
41060 this.setPad = function (str) {
41063 }; // set custom tab string characters
41066 this.setTab = function (str) {
41071 this.setUTF8 = function (bool) {
41072 if (typeof bool === 'boolean') {
41081 * CRC-32 calculation
41085 * @param {String} str Input String
41088 CRC32: function CRC32(str) {
41095 str = utf8Encode(str);
41096 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('');
41099 for (i = 0, iTop = str.length; i < iTop; i += 1) {
41100 y = (crc ^ str.charCodeAt(i)) & 0xFF;
41101 x = '0x' + table.substr(y * 9, 8);
41102 crc = crc >>> 8 ^ x;
41103 } // always return a positive number (that's what >>> 0 does)
41106 return (crc ^ -1) >>> 0;
41113 * @param {Object} [config]
41115 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
41116 * Digest Algorithm, as defined in RFC 1321.
41117 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
41118 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41119 * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
41121 MD5: function MD5(options) {
41123 * Private config properties. You may need to tweak these to be compatible with
41124 * the server-side, but the defaults work in most cases.
41125 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
41127 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41128 // hexadecimal output case format. false - lowercase; true - uppercase
41129 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41130 // base-64 pad character. Defaults to '=' for strict RFC compliance
41131 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
41132 // privileged (public) methods
41134 this.hex = function (s) {
41135 return rstr2hex(rstr(s), hexcase);
41138 this.b64 = function (s) {
41139 return rstr2b64(rstr(s), b64pad);
41142 this.any = function (s, e) {
41143 return rstr2any(rstr(s), e);
41146 this.raw = function (s) {
41150 this.hex_hmac = function (k, d) {
41151 return rstr2hex(rstr_hmac(k, d), hexcase);
41154 this.b64_hmac = function (k, d) {
41155 return rstr2b64(rstr_hmac(k, d), b64pad);
41158 this.any_hmac = function (k, d, e) {
41159 return rstr2any(rstr_hmac(k, d), e);
41162 * Perform a simple self-test to see if the VM is working
41163 * @return {String} Hexadecimal hash sample
41167 this.vm_test = function () {
41168 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41171 * Enable/disable uppercase hexadecimal returned string
41173 * @return {Object} this
41177 this.setUpperCase = function (a) {
41178 if (typeof a === 'boolean') {
41185 * Defines a base64 pad string
41186 * @param {String} Pad
41187 * @return {Object} this
41191 this.setPad = function (a) {
41192 b64pad = a || b64pad;
41196 * Defines a base64 pad string
41198 * @return {Object} [this]
41202 this.setUTF8 = function (a) {
41203 if (typeof a === 'boolean') {
41208 }; // private methods
41211 * Calculate the MD5 of a raw string
41216 s = utf8 ? utf8Encode(s) : s;
41217 return binl2rstr(binl(rstr2binl(s), s.length * 8));
41220 * Calculate the HMAC-MD5, of a key and some data (raw strings)
41224 function rstr_hmac(key, data) {
41225 var bkey, ipad, opad, hash, i;
41226 key = utf8 ? utf8Encode(key) : key;
41227 data = utf8 ? utf8Encode(data) : data;
41228 bkey = rstr2binl(key);
41230 if (bkey.length > 16) {
41231 bkey = binl(bkey, key.length * 8);
41234 ipad = Array(16), opad = Array(16);
41236 for (i = 0; i < 16; i += 1) {
41237 ipad[i] = bkey[i] ^ 0x36363636;
41238 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41241 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
41242 return binl2rstr(binl(opad.concat(hash), 512 + 128));
41245 * Calculate the MD5 of an array of little-endian words, and a bit length.
41249 function binl(x, len) {
41259 /* append padding */
41261 x[len >> 5] |= 0x80 << len % 32;
41262 x[(len + 64 >>> 9 << 4) + 14] = len;
41264 for (i = 0; i < x.length; i += 16) {
41269 a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
41270 d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
41271 c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
41272 b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
41273 a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
41274 d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
41275 c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
41276 b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
41277 a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
41278 d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
41279 c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
41280 b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
41281 a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
41282 d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
41283 c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
41284 b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
41285 a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
41286 d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
41287 c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
41288 b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
41289 a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
41290 d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
41291 c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
41292 b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
41293 a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
41294 d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
41295 c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
41296 b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
41297 a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
41298 d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
41299 c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
41300 b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
41301 a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
41302 d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
41303 c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
41304 b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
41305 a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
41306 d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
41307 c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
41308 b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
41309 a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
41310 d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
41311 c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
41312 b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
41313 a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
41314 d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
41315 c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
41316 b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
41317 a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
41318 d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
41319 c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
41320 b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
41321 a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
41322 d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
41323 c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
41324 b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
41325 a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
41326 d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
41327 c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
41328 b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
41329 a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
41330 d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
41331 c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
41332 b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
41333 a = safe_add(a, olda);
41334 b = safe_add(b, oldb);
41335 c = safe_add(c, oldc);
41336 d = safe_add(d, oldd);
41339 return Array(a, b, c, d);
41342 * These functions implement the four basic operations the algorithm uses.
41346 function md5_cmn(q, a, b, x, s, t) {
41347 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
41350 function md5_ff(a, b, c, d, x, s, t) {
41351 return md5_cmn(b & c | ~b & d, a, b, x, s, t);
41354 function md5_gg(a, b, c, d, x, s, t) {
41355 return md5_cmn(b & d | c & ~d, a, b, x, s, t);
41358 function md5_hh(a, b, c, d, x, s, t) {
41359 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
41362 function md5_ii(a, b, c, d, x, s, t) {
41363 return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
41369 * @class Hashes.SHA1
41370 * @param {Object} [config]
41373 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
41374 * Version 2.2 Copyright Paul Johnston 2000 - 2009.
41375 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41376 * See http://pajhome.org.uk/crypt/md5 for details.
41378 SHA1: function SHA1(options) {
41380 * Private config properties. You may need to tweak these to be compatible with
41381 * the server-side, but the defaults work in most cases.
41382 * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
41384 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41385 // hexadecimal output case format. false - lowercase; true - uppercase
41386 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41387 // base-64 pad character. Defaults to '=' for strict RFC compliance
41388 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true; // enable/disable utf8 encoding
41391 this.hex = function (s) {
41392 return rstr2hex(rstr(s), hexcase);
41395 this.b64 = function (s) {
41396 return rstr2b64(rstr(s), b64pad);
41399 this.any = function (s, e) {
41400 return rstr2any(rstr(s), e);
41403 this.raw = function (s) {
41407 this.hex_hmac = function (k, d) {
41408 return rstr2hex(rstr_hmac(k, d));
41411 this.b64_hmac = function (k, d) {
41412 return rstr2b64(rstr_hmac(k, d), b64pad);
41415 this.any_hmac = function (k, d, e) {
41416 return rstr2any(rstr_hmac(k, d), e);
41419 * Perform a simple self-test to see if the VM is working
41420 * @return {String} Hexadecimal hash sample
41425 this.vm_test = function () {
41426 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41429 * @description Enable/disable uppercase hexadecimal returned string
41431 * @return {Object} this
41436 this.setUpperCase = function (a) {
41437 if (typeof a === 'boolean') {
41444 * @description Defines a base64 pad string
41445 * @param {string} Pad
41446 * @return {Object} this
41451 this.setPad = function (a) {
41452 b64pad = a || b64pad;
41456 * @description Defines a base64 pad string
41458 * @return {Object} this
41463 this.setUTF8 = function (a) {
41464 if (typeof a === 'boolean') {
41469 }; // private methods
41472 * Calculate the SHA-512 of a raw string
41477 s = utf8 ? utf8Encode(s) : s;
41478 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41481 * Calculate the HMAC-SHA1 of a key and some data (raw strings)
41485 function rstr_hmac(key, data) {
41486 var bkey, ipad, opad, i, hash;
41487 key = utf8 ? utf8Encode(key) : key;
41488 data = utf8 ? utf8Encode(data) : data;
41489 bkey = rstr2binb(key);
41491 if (bkey.length > 16) {
41492 bkey = binb(bkey, key.length * 8);
41495 ipad = Array(16), opad = Array(16);
41497 for (i = 0; i < 16; i += 1) {
41498 ipad[i] = bkey[i] ^ 0x36363636;
41499 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41502 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
41503 return binb2rstr(binb(opad.concat(hash), 512 + 160));
41506 * Calculate the SHA-1 of an array of big-endian words, and a bit length
41510 function binb(x, len) {
41525 /* append padding */
41527 x[len >> 5] |= 0x80 << 24 - len % 32;
41528 x[(len + 64 >> 9 << 4) + 15] = len;
41530 for (i = 0; i < x.length; i += 16) {
41537 for (j = 0; j < 80; j += 1) {
41541 w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
41544 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)));
41547 c = bit_rol(b, 30);
41552 a = safe_add(a, olda);
41553 b = safe_add(b, oldb);
41554 c = safe_add(c, oldc);
41555 d = safe_add(d, oldd);
41556 e = safe_add(e, olde);
41559 return Array(a, b, c, d, e);
41562 * Perform the appropriate triplet combination function for the current
41567 function sha1_ft(t, b, c, d) {
41569 return b & c | ~b & d;
41577 return b & c | b & d | c & d;
41583 * Determine the appropriate additive constant for the current iteration
41587 function sha1_kt(t) {
41588 return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
41593 * @class Hashes.SHA256
41596 * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
41597 * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
41598 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41599 * See http://pajhome.org.uk/crypt/md5 for details.
41600 * Also http://anmar.eu.org/projects/jssha2/
41602 SHA256: function SHA256(options) {
41604 * Private properties configuration variables. You may need to tweak these to be compatible with
41605 * the server-side, but the defaults work in most cases.
41606 * @see this.setUpperCase() method
41607 * @see this.setPad() method
41609 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41610 // hexadecimal output case format. false - lowercase; true - uppercase */
41611 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41613 /* base-64 pad character. Default '=' for strict RFC compliance */
41614 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
41616 /* enable/disable utf8 encoding */
41618 /* privileged (public) methods */
41620 this.hex = function (s) {
41621 return rstr2hex(rstr(s, utf8));
41624 this.b64 = function (s) {
41625 return rstr2b64(rstr(s, utf8), b64pad);
41628 this.any = function (s, e) {
41629 return rstr2any(rstr(s, utf8), e);
41632 this.raw = function (s) {
41633 return rstr(s, utf8);
41636 this.hex_hmac = function (k, d) {
41637 return rstr2hex(rstr_hmac(k, d));
41640 this.b64_hmac = function (k, d) {
41641 return rstr2b64(rstr_hmac(k, d), b64pad);
41644 this.any_hmac = function (k, d, e) {
41645 return rstr2any(rstr_hmac(k, d), e);
41648 * Perform a simple self-test to see if the VM is working
41649 * @return {String} Hexadecimal hash sample
41654 this.vm_test = function () {
41655 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41658 * Enable/disable uppercase hexadecimal returned string
41660 * @return {Object} this
41665 this.setUpperCase = function (a) {
41666 if (typeof a === 'boolean') {
41673 * @description Defines a base64 pad string
41674 * @param {string} Pad
41675 * @return {Object} this
41680 this.setPad = function (a) {
41681 b64pad = a || b64pad;
41685 * Defines a base64 pad string
41687 * @return {Object} this
41692 this.setUTF8 = function (a) {
41693 if (typeof a === 'boolean') {
41698 }; // private methods
41701 * Calculate the SHA-512 of a raw string
41705 function rstr(s, utf8) {
41706 s = utf8 ? utf8Encode(s) : s;
41707 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41710 * Calculate the HMAC-sha256 of a key and some data (raw strings)
41714 function rstr_hmac(key, data) {
41715 key = utf8 ? utf8Encode(key) : key;
41716 data = utf8 ? utf8Encode(data) : data;
41719 bkey = rstr2binb(key),
41723 if (bkey.length > 16) {
41724 bkey = binb(bkey, key.length * 8);
41727 for (; i < 16; i += 1) {
41728 ipad[i] = bkey[i] ^ 0x36363636;
41729 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41732 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
41733 return binb2rstr(binb(opad.concat(hash), 512 + 256));
41736 * Main sha256 function, with its support functions
41740 function sha256_S(X, n) {
41741 return X >>> n | X << 32 - n;
41744 function sha256_R(X, n) {
41748 function sha256_Ch(x, y, z) {
41749 return x & y ^ ~x & z;
41752 function sha256_Maj(x, y, z) {
41753 return x & y ^ x & z ^ y & z;
41756 function sha256_Sigma0256(x) {
41757 return sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22);
41760 function sha256_Sigma1256(x) {
41761 return sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25);
41764 function sha256_Gamma0256(x) {
41765 return sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3);
41768 function sha256_Gamma1256(x) {
41769 return sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10);
41772 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];
41774 function binb(m, l) {
41775 var HASH = [1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225];
41776 var W = new Array(64);
41777 var a, b, c, d, e, f, g, h;
41779 /* append padding */
41781 m[l >> 5] |= 0x80 << 24 - l % 32;
41782 m[(l + 64 >> 9 << 4) + 15] = l;
41784 for (i = 0; i < m.length; i += 16) {
41794 for (j = 0; j < 64; j += 1) {
41798 W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]), sha256_Gamma0256(W[j - 15])), W[j - 16]);
41801 T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)), sha256_K[j]), W[j]);
41802 T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
41806 e = safe_add(d, T1);
41810 a = safe_add(T1, T2);
41813 HASH[0] = safe_add(a, HASH[0]);
41814 HASH[1] = safe_add(b, HASH[1]);
41815 HASH[2] = safe_add(c, HASH[2]);
41816 HASH[3] = safe_add(d, HASH[3]);
41817 HASH[4] = safe_add(e, HASH[4]);
41818 HASH[5] = safe_add(f, HASH[5]);
41819 HASH[6] = safe_add(g, HASH[6]);
41820 HASH[7] = safe_add(h, HASH[7]);
41828 * @class Hashes.SHA512
41831 * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
41832 * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
41833 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
41834 * See http://pajhome.org.uk/crypt/md5 for details.
41836 SHA512: function SHA512(options) {
41838 * Private properties configuration variables. You may need to tweak these to be compatible with
41839 * the server-side, but the defaults work in most cases.
41840 * @see this.setUpperCase() method
41841 * @see this.setPad() method
41843 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
41845 /* hexadecimal output case format. false - lowercase; true - uppercase */
41846 b64pad = options && typeof options.pad === 'string' ? options.pad : '=',
41848 /* base-64 pad character. Default '=' for strict RFC compliance */
41849 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
41851 /* enable/disable utf8 encoding */
41853 /* privileged (public) methods */
41855 this.hex = function (s) {
41856 return rstr2hex(rstr(s));
41859 this.b64 = function (s) {
41860 return rstr2b64(rstr(s), b64pad);
41863 this.any = function (s, e) {
41864 return rstr2any(rstr(s), e);
41867 this.raw = function (s) {
41871 this.hex_hmac = function (k, d) {
41872 return rstr2hex(rstr_hmac(k, d));
41875 this.b64_hmac = function (k, d) {
41876 return rstr2b64(rstr_hmac(k, d), b64pad);
41879 this.any_hmac = function (k, d, e) {
41880 return rstr2any(rstr_hmac(k, d), e);
41883 * Perform a simple self-test to see if the VM is working
41884 * @return {String} Hexadecimal hash sample
41889 this.vm_test = function () {
41890 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
41893 * @description Enable/disable uppercase hexadecimal returned string
41895 * @return {Object} this
41900 this.setUpperCase = function (a) {
41901 if (typeof a === 'boolean') {
41908 * @description Defines a base64 pad string
41909 * @param {string} Pad
41910 * @return {Object} this
41915 this.setPad = function (a) {
41916 b64pad = a || b64pad;
41920 * @description Defines a base64 pad string
41922 * @return {Object} this
41927 this.setUTF8 = function (a) {
41928 if (typeof a === 'boolean') {
41934 /* private methods */
41937 * Calculate the SHA-512 of a raw string
41942 s = utf8 ? utf8Encode(s) : s;
41943 return binb2rstr(binb(rstr2binb(s), s.length * 8));
41946 * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
41950 function rstr_hmac(key, data) {
41951 key = utf8 ? utf8Encode(key) : key;
41952 data = utf8 ? utf8Encode(data) : data;
41955 bkey = rstr2binb(key),
41959 if (bkey.length > 32) {
41960 bkey = binb(bkey, key.length * 8);
41963 for (; i < 32; i += 1) {
41964 ipad[i] = bkey[i] ^ 0x36363636;
41965 opad[i] = bkey[i] ^ 0x5C5C5C5C;
41968 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
41969 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
41972 * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
41976 function binb(x, len) {
41981 hash = new Array(16),
41982 //Initial hash values
41983 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)],
41984 T1 = new int64(0, 0),
41985 T2 = new int64(0, 0),
41986 a = new int64(0, 0),
41987 b = new int64(0, 0),
41988 c = new int64(0, 0),
41989 d = new int64(0, 0),
41990 e = new int64(0, 0),
41991 f = new int64(0, 0),
41992 g = new int64(0, 0),
41993 h = new int64(0, 0),
41994 //Temporary variables not specified by the document
41995 s0 = new int64(0, 0),
41996 s1 = new int64(0, 0),
41997 Ch = new int64(0, 0),
41998 Maj = new int64(0, 0),
41999 r1 = new int64(0, 0),
42000 r2 = new int64(0, 0),
42001 r3 = new int64(0, 0);
42003 if (sha512_k === undefined) {
42005 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)];
42008 for (i = 0; i < 80; i += 1) {
42009 W[i] = new int64(0, 0);
42010 } // append padding to the source string. The format is described in the FIPS.
42013 x[len >> 5] |= 0x80 << 24 - (len & 0x1f);
42014 x[(len + 128 >> 10 << 5) + 31] = len;
42017 for (i = 0; i < l; i += 32) {
42018 //32 dwords is the block size
42019 int64copy(a, H[0]);
42020 int64copy(b, H[1]);
42021 int64copy(c, H[2]);
42022 int64copy(d, H[3]);
42023 int64copy(e, H[4]);
42024 int64copy(f, H[5]);
42025 int64copy(g, H[6]);
42026 int64copy(h, H[7]);
42028 for (j = 0; j < 16; j += 1) {
42029 W[j].h = x[i + 2 * j];
42030 W[j].l = x[i + 2 * j + 1];
42033 for (j = 16; j < 80; j += 1) {
42035 int64rrot(r1, W[j - 2], 19);
42036 int64revrrot(r2, W[j - 2], 29);
42037 int64shr(r3, W[j - 2], 6);
42038 s1.l = r1.l ^ r2.l ^ r3.l;
42039 s1.h = r1.h ^ r2.h ^ r3.h; //sigma0
42041 int64rrot(r1, W[j - 15], 1);
42042 int64rrot(r2, W[j - 15], 8);
42043 int64shr(r3, W[j - 15], 7);
42044 s0.l = r1.l ^ r2.l ^ r3.l;
42045 s0.h = r1.h ^ r2.h ^ r3.h;
42046 int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
42049 for (j = 0; j < 80; j += 1) {
42051 Ch.l = e.l & f.l ^ ~e.l & g.l;
42052 Ch.h = e.h & f.h ^ ~e.h & g.h; //Sigma1
42054 int64rrot(r1, e, 14);
42055 int64rrot(r2, e, 18);
42056 int64revrrot(r3, e, 9);
42057 s1.l = r1.l ^ r2.l ^ r3.l;
42058 s1.h = r1.h ^ r2.h ^ r3.h; //Sigma0
42060 int64rrot(r1, a, 28);
42061 int64revrrot(r2, a, 2);
42062 int64revrrot(r3, a, 7);
42063 s0.l = r1.l ^ r2.l ^ r3.l;
42064 s0.h = r1.h ^ r2.h ^ r3.h; //Maj
42066 Maj.l = a.l & b.l ^ a.l & c.l ^ b.l & c.l;
42067 Maj.h = a.h & b.h ^ a.h & c.h ^ b.h & c.h;
42068 int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
42069 int64add(T2, s0, Maj);
42073 int64add(e, d, T1);
42077 int64add(a, T1, T2);
42080 int64add(H[0], H[0], a);
42081 int64add(H[1], H[1], b);
42082 int64add(H[2], H[2], c);
42083 int64add(H[3], H[3], d);
42084 int64add(H[4], H[4], e);
42085 int64add(H[5], H[5], f);
42086 int64add(H[6], H[6], g);
42087 int64add(H[7], H[7], h);
42088 } //represent the hash as an array of 32-bit dwords
42091 for (i = 0; i < 8; i += 1) {
42092 hash[2 * i] = H[i].h;
42093 hash[2 * i + 1] = H[i].l;
42097 } //A constructor for 64-bit numbers
42100 function int64(h, l) {
42102 this.l = l; //this.toString = int64toString;
42103 } //Copies src into dst, assuming both are 64-bit numbers
42106 function int64copy(dst, src) {
42109 } //Right-rotates a 64-bit number by shift
42110 //Won't handle cases of shift>=32
42111 //The function revrrot() is for that
42114 function int64rrot(dst, x, shift) {
42115 dst.l = x.l >>> shift | x.h << 32 - shift;
42116 dst.h = x.h >>> shift | x.l << 32 - shift;
42117 } //Reverses the dwords of the source and then rotates right by shift.
42118 //This is equivalent to rotation by 32+shift
42121 function int64revrrot(dst, x, shift) {
42122 dst.l = x.h >>> shift | x.l << 32 - shift;
42123 dst.h = x.l >>> shift | x.h << 32 - shift;
42124 } //Bitwise-shifts right a 64-bit number by shift
42125 //Won't handle shift>=32, but it's never needed in SHA512
42128 function int64shr(dst, x, shift) {
42129 dst.l = x.l >>> shift | x.h << 32 - shift;
42130 dst.h = x.h >>> shift;
42131 } //Adds two 64-bit numbers
42132 //Like the original implementation, does not rely on 32-bit operations
42135 function int64add(dst, x, y) {
42136 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
42137 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
42138 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
42139 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
42140 dst.l = w0 & 0xffff | w1 << 16;
42141 dst.h = w2 & 0xffff | w3 << 16;
42142 } //Same, except with 4 addends. Works faster than adding them one by one.
42145 function int64add4(dst, a, b, c, d) {
42146 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
42147 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
42148 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
42149 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
42150 dst.l = w0 & 0xffff | w1 << 16;
42151 dst.h = w2 & 0xffff | w3 << 16;
42152 } //Same, except with 5 addends
42155 function int64add5(dst, a, b, c, d, e) {
42156 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
42157 w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
42158 w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
42159 w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
42160 dst.l = w0 & 0xffff | w1 << 16;
42161 dst.h = w2 & 0xffff | w3 << 16;
42166 * @class Hashes.RMD160
42168 * @param {Object} [config]
42170 * A JavaScript implementation of the RIPEMD-160 Algorithm
42171 * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
42172 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
42173 * See http://pajhome.org.uk/crypt/md5 for details.
42174 * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
42176 RMD160: function RMD160(options) {
42178 * Private properties configuration variables. You may need to tweak these to be compatible with
42179 * the server-side, but the defaults work in most cases.
42180 * @see this.setUpperCase() method
42181 * @see this.setPad() method
42183 var hexcase = options && typeof options.uppercase === 'boolean' ? options.uppercase : false,
42185 /* hexadecimal output case format. false - lowercase; true - uppercase */
42186 b64pad = options && typeof options.pad === 'string' ? options.pa : '=',
42188 /* base-64 pad character. Default '=' for strict RFC compliance */
42189 utf8 = options && typeof options.utf8 === 'boolean' ? options.utf8 : true,
42191 /* enable/disable utf8 encoding */
42192 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],
42193 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],
42194 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],
42195 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];
42196 /* privileged (public) methods */
42198 this.hex = function (s) {
42199 return rstr2hex(rstr(s));
42202 this.b64 = function (s) {
42203 return rstr2b64(rstr(s), b64pad);
42206 this.any = function (s, e) {
42207 return rstr2any(rstr(s), e);
42210 this.raw = function (s) {
42214 this.hex_hmac = function (k, d) {
42215 return rstr2hex(rstr_hmac(k, d));
42218 this.b64_hmac = function (k, d) {
42219 return rstr2b64(rstr_hmac(k, d), b64pad);
42222 this.any_hmac = function (k, d, e) {
42223 return rstr2any(rstr_hmac(k, d), e);
42226 * Perform a simple self-test to see if the VM is working
42227 * @return {String} Hexadecimal hash sample
42232 this.vm_test = function () {
42233 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
42236 * @description Enable/disable uppercase hexadecimal returned string
42238 * @return {Object} this
42243 this.setUpperCase = function (a) {
42244 if (typeof a === 'boolean') {
42251 * @description Defines a base64 pad string
42252 * @param {string} Pad
42253 * @return {Object} this
42258 this.setPad = function (a) {
42259 if (typeof a !== 'undefined') {
42266 * @description Defines a base64 pad string
42268 * @return {Object} this
42273 this.setUTF8 = function (a) {
42274 if (typeof a === 'boolean') {
42280 /* private methods */
42283 * Calculate the rmd160 of a raw string
42288 s = utf8 ? utf8Encode(s) : s;
42289 return binl2rstr(binl(rstr2binl(s), s.length * 8));
42292 * Calculate the HMAC-rmd160 of a key and some data (raw strings)
42296 function rstr_hmac(key, data) {
42297 key = utf8 ? utf8Encode(key) : key;
42298 data = utf8 ? utf8Encode(data) : data;
42301 bkey = rstr2binl(key),
42305 if (bkey.length > 16) {
42306 bkey = binl(bkey, key.length * 8);
42309 for (i = 0; i < 16; i += 1) {
42310 ipad[i] = bkey[i] ^ 0x36363636;
42311 opad[i] = bkey[i] ^ 0x5C5C5C5C;
42314 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
42315 return binl2rstr(binl(opad.concat(hash), 512 + 160));
42318 * Convert an array of little-endian words to a string
42322 function binl2rstr(input) {
42325 l = input.length * 32;
42327 for (i = 0; i < l; i += 8) {
42328 output += String.fromCharCode(input[i >> 5] >>> i % 32 & 0xFF);
42334 * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
42338 function binl(x, len) {
42358 /* append padding */
42360 x[len >> 5] |= 0x80 << len % 32;
42361 x[(len + 64 >>> 9 << 4) + 14] = len;
42364 for (i = 0; i < l; i += 16) {
42371 for (j = 0; j <= 79; j += 1) {
42372 T = safe_add(A1, rmd160_f(j, B1, C1, D1));
42373 T = safe_add(T, x[i + rmd160_r1[j]]);
42374 T = safe_add(T, rmd160_K1(j));
42375 T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
42378 D1 = bit_rol(C1, 10);
42381 T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
42382 T = safe_add(T, x[i + rmd160_r2[j]]);
42383 T = safe_add(T, rmd160_K2(j));
42384 T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
42387 D2 = bit_rol(C2, 10);
42392 T = safe_add(h1, safe_add(C1, D2));
42393 h1 = safe_add(h2, safe_add(D1, E2));
42394 h2 = safe_add(h3, safe_add(E1, A2));
42395 h3 = safe_add(h4, safe_add(A1, B2));
42396 h4 = safe_add(h0, safe_add(B1, C2));
42400 return [h0, h1, h2, h3, h4];
42401 } // specific algorithm methods
42404 function rmd160_f(j, x, y, z) {
42405 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';
42408 function rmd160_K1(j) {
42409 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';
42412 function rmd160_K2(j) {
42413 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';
42416 }; // exposes Hashes
42418 (function (window, undefined$1) {
42419 var freeExports = false;
42422 freeExports = exports;
42424 if (exports && _typeof(commonjsGlobal) === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
42425 window = commonjsGlobal;
42429 if (typeof undefined$1 === 'function' && _typeof(undefined$1.amd) === 'object' && undefined$1.amd) {
42430 // define as an anonymous module, so, through path mapping, it can be aliased
42431 undefined$1(function () {
42434 } else if (freeExports) {
42435 // in Node.js or RingoJS v0.8.0+
42436 if ( module && module.exports === freeExports) {
42437 module.exports = Hashes;
42438 } // in Narwhal or RingoJS v0.7.0-
42440 freeExports.Hashes = Hashes;
42443 // in a browser or Rhino
42444 window.Hashes = Hashes;
42451 var immutable = extend$2;
42452 var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
42454 function extend$2() {
42457 for (var i = 0; i < arguments.length; i++) {
42458 var source = arguments[i];
42460 for (var key in source) {
42461 if (hasOwnProperty$2.call(source, key)) {
42462 target[key] = source[key];
42470 var sha1 = new hashes.SHA1();
42473 ohauth.qsString = function (obj) {
42474 return Object.keys(obj).sort().map(function (key) {
42475 return ohauth.percentEncode(key) + '=' + ohauth.percentEncode(obj[key]);
42479 ohauth.stringQs = function (str) {
42480 return str.split('&').filter(function (pair) {
42481 return pair !== '';
42482 }).reduce(function (obj, pair) {
42483 var parts = pair.split('=');
42484 obj[decodeURIComponent(parts[0])] = null === parts[1] ? '' : decodeURIComponent(parts[1]);
42489 ohauth.rawxhr = function (method, url, data, headers, callback) {
42490 var xhr = new XMLHttpRequest(),
42491 twoHundred = /^20\d$/;
42493 xhr.onreadystatechange = function () {
42494 if (4 === xhr.readyState && 0 !== xhr.status) {
42495 if (twoHundred.test(xhr.status)) callback(null, xhr);else return callback(xhr, null);
42499 xhr.onerror = function (e) {
42500 return callback(e, null);
42503 xhr.open(method, url, true);
42505 for (var h in headers) {
42506 xhr.setRequestHeader(h, headers[h]);
42513 ohauth.xhr = function (method, url, auth, data, options, callback) {
42514 var headers = options && options.header || {
42515 'Content-Type': 'application/x-www-form-urlencoded'
42517 headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
42518 return ohauth.rawxhr(method, url, data, headers, callback);
42521 ohauth.nonce = function () {
42522 for (var o = ''; o.length < 6;) {
42523 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
42529 ohauth.authHeader = function (obj) {
42530 return Object.keys(obj).sort().map(function (key) {
42531 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
42535 ohauth.timestamp = function () {
42536 return ~~(+new Date() / 1000);
42539 ohauth.percentEncode = function (s) {
42540 return encodeURIComponent(s).replace(/\!/g, '%21').replace(/\'/g, '%27').replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
42543 ohauth.baseString = function (method, url, params) {
42544 if (params.oauth_signature) delete params.oauth_signature;
42545 return [method, ohauth.percentEncode(url), ohauth.percentEncode(ohauth.qsString(params))].join('&');
42548 ohauth.signature = function (oauth_secret, token_secret, baseString) {
42549 return sha1.b64_hmac(ohauth.percentEncode(oauth_secret) + '&' + ohauth.percentEncode(token_secret), baseString);
42552 * Takes an options object for configuration (consumer_key,
42553 * consumer_secret, version, signature_method, token, token_secret)
42554 * and returns a function that generates the Authorization header
42557 * The returned function takes these parameters:
42558 * - method: GET/POST/...
42559 * - uri: full URI with protocol, port, path and query string
42560 * - extra_params: any extra parameters (that are passed in the POST data),
42561 * can be an object or a from-urlencoded string.
42563 * Returned function returns full OAuth header with "OAuth" string in it.
42567 ohauth.headerGenerator = function (options) {
42568 options = options || {};
42569 var consumer_key = options.consumer_key || '',
42570 consumer_secret = options.consumer_secret || '',
42571 signature_method = options.signature_method || 'HMAC-SHA1',
42572 version = options.version || '1.0',
42573 token = options.token || '',
42574 token_secret = options.token_secret || '';
42575 return function (method, uri, extra_params) {
42576 method = method.toUpperCase();
42578 if (typeof extra_params === 'string' && extra_params.length > 0) {
42579 extra_params = ohauth.stringQs(extra_params);
42582 var uri_parts = uri.split('?', 2),
42583 base_uri = uri_parts[0];
42584 var query_params = uri_parts.length === 2 ? ohauth.stringQs(uri_parts[1]) : {};
42585 var oauth_params = {
42586 oauth_consumer_key: consumer_key,
42587 oauth_signature_method: signature_method,
42588 oauth_version: version,
42589 oauth_timestamp: ohauth.timestamp(),
42590 oauth_nonce: ohauth.nonce()
42592 if (token) oauth_params.oauth_token = token;
42593 var all_params = immutable({}, oauth_params, query_params, extra_params),
42594 base_str = ohauth.baseString(method, base_uri, all_params);
42595 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
42596 return 'OAuth ' + ohauth.authHeader(oauth_params);
42600 var ohauth_1 = ohauth;
42602 var resolveUrl$1 = createCommonjsModule(function (module, exports) {
42603 // Copyright 2014 Simon Lydell
42604 // X11 (“MIT”) Licensed. (See LICENSE.)
42605 void function (root, factory) {
42607 module.exports = factory();
42609 }(commonjsGlobal, function () {
42610 function resolveUrl()
42613 var numUrls = arguments.length;
42615 if (numUrls === 0) {
42616 throw new Error("resolveUrl requires at least one argument; got none.");
42619 var base = document.createElement("base");
42620 base.href = arguments[0];
42622 if (numUrls === 1) {
42626 var head = document.getElementsByTagName("head")[0];
42627 head.insertBefore(base, head.firstChild);
42628 var a = document.createElement("a");
42631 for (var index = 1; index < numUrls; index++) {
42632 a.href = arguments[index];
42634 base.href = resolved;
42637 head.removeChild(base);
42645 var assign = make_assign();
42646 var create$1 = make_create();
42647 var trim$3 = make_trim();
42648 var Global = typeof window !== 'undefined' ? window : commonjsGlobal;
42659 isFunction: isFunction,
42660 isObject: isObject$2,
42664 function make_assign() {
42665 if (Object.assign) {
42666 return Object.assign;
42668 return function shimAssign(obj, props1, props2, etc) {
42669 for (var i = 1; i < arguments.length; i++) {
42670 each(Object(arguments[i]), function (val, key) {
42680 function make_create() {
42681 if (Object.create) {
42682 return function create(obj, assignProps1, assignProps2, etc) {
42683 var assignArgsList = slice$2(arguments, 1);
42684 return assign.apply(this, [Object.create(obj)].concat(assignArgsList));
42687 var F = function F() {}; // eslint-disable-line no-inner-declarations
42690 return function create(obj, assignProps1, assignProps2, etc) {
42691 var assignArgsList = slice$2(arguments, 1);
42693 return assign.apply(this, [new F()].concat(assignArgsList));
42698 function make_trim() {
42699 if (String.prototype.trim) {
42700 return function trim(str) {
42701 return String.prototype.trim.call(str);
42704 return function trim(str) {
42705 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
42710 function bind$1(obj, fn) {
42711 return function () {
42712 return fn.apply(obj, Array.prototype.slice.call(arguments, 0));
42716 function slice$2(arr, index) {
42717 return Array.prototype.slice.call(arr, index || 0);
42720 function each(obj, fn) {
42721 pluck(obj, function (val, key) {
42727 function map$1(obj, fn) {
42728 var res = isList(obj) ? [] : {};
42729 pluck(obj, function (v, k) {
42736 function pluck(obj, fn) {
42738 for (var i = 0; i < obj.length; i++) {
42739 if (fn(obj[i], i)) {
42744 for (var key in obj) {
42745 if (obj.hasOwnProperty(key)) {
42746 if (fn(obj[key], key)) {
42754 function isList(val) {
42755 return val != null && typeof val != 'function' && typeof val.length == 'number';
42758 function isFunction(val) {
42759 return val && {}.toString.call(val) === '[object Function]';
42762 function isObject$2(val) {
42763 return val && {}.toString.call(val) === '[object Object]';
42766 var slice$3 = util.slice;
42767 var pluck$1 = util.pluck;
42768 var each$1 = util.each;
42769 var bind$2 = util.bind;
42770 var create$2 = util.create;
42771 var isList$1 = util.isList;
42772 var isFunction$1 = util.isFunction;
42773 var isObject$3 = util.isObject;
42774 var storeEngine = {
42775 createStore: _createStore
42780 // get returns the value of the given key. If that value
42781 // is undefined, it returns optionalDefaultValue instead.
42782 get: function get(key, optionalDefaultValue) {
42783 var data = this.storage.read(this._namespacePrefix + key);
42784 return this._deserialize(data, optionalDefaultValue);
42786 // set will store the given value at key and returns value.
42787 // Calling set with value === undefined is equivalent to calling remove.
42788 set: function set(key, value) {
42789 if (value === undefined) {
42790 return this.remove(key);
42793 this.storage.write(this._namespacePrefix + key, this._serialize(value));
42796 // remove deletes the key and value stored at the given key.
42797 remove: function remove(key) {
42798 this.storage.remove(this._namespacePrefix + key);
42800 // each will call the given callback once for each key-value pair
42802 each: function each(callback) {
42804 this.storage.each(function (val, namespacedKey) {
42805 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
42808 // clearAll will remove all the stored key-value pairs in this store.
42809 clearAll: function clearAll() {
42810 this.storage.clearAll();
42812 // additional functionality that can't live in plugins
42813 // ---------------------------------------------------
42814 // hasNamespace returns true if this store instance has the given namespace.
42815 hasNamespace: function hasNamespace(namespace) {
42816 return this._namespacePrefix == '__storejs_' + namespace + '_';
42818 // createStore creates a store.js instance with the first
42819 // functioning storage in the list of storage candidates,
42820 // and applies the the given mixins to the instance.
42821 createStore: function createStore() {
42822 return _createStore.apply(this, arguments);
42824 addPlugin: function addPlugin(plugin) {
42825 this._addPlugin(plugin);
42827 namespace: function namespace(_namespace) {
42828 return _createStore(this.storage, this.plugins, _namespace);
42833 var _console = typeof console == 'undefined' ? null : console;
42839 var fn = _console.warn ? _console.warn : _console.log;
42840 fn.apply(_console, arguments);
42843 function _createStore(storages, plugins, namespace) {
42848 if (storages && !isList$1(storages)) {
42849 storages = [storages];
42852 if (plugins && !isList$1(plugins)) {
42853 plugins = [plugins];
42856 var namespacePrefix = namespace ? '__storejs_' + namespace + '_' : '';
42857 var namespaceRegexp = namespace ? new RegExp('^' + namespacePrefix) : null;
42858 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
42860 if (!legalNamespaces.test(namespace)) {
42861 throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes');
42864 var _privateStoreProps = {
42865 _namespacePrefix: namespacePrefix,
42866 _namespaceRegexp: namespaceRegexp,
42867 _testStorage: function _testStorage(storage) {
42869 var testStr = '__storejs__test__';
42870 storage.write(testStr, testStr);
42871 var ok = storage.read(testStr) === testStr;
42872 storage.remove(testStr);
42878 _assignPluginFnProp: function _assignPluginFnProp(pluginFnProp, propName) {
42879 var oldFn = this[propName];
42881 this[propName] = function pluginFn() {
42882 var args = slice$3(arguments, 0);
42883 var self = this; // super_fn calls the old function which was overwritten by
42886 function super_fn() {
42891 each$1(arguments, function (arg, i) {
42894 return oldFn.apply(self, args);
42895 } // Give mixing function access to super_fn by prefixing all mixin function
42896 // arguments with super_fn.
42899 var newFnArgs = [super_fn].concat(args);
42900 return pluginFnProp.apply(self, newFnArgs);
42903 _serialize: function _serialize(obj) {
42904 return JSON.stringify(obj);
42906 _deserialize: function _deserialize(strVal, defaultVal) {
42909 } // It is possible that a raw string value has been previously stored
42910 // in a storage without using store.js, meaning it will be a raw
42911 // string value instead of a JSON serialized string. By defaulting
42912 // to the raw string value in case of a JSON parse error, we allow
42913 // for past stored values to be forwards-compatible with store.js
42919 val = JSON.parse(strVal);
42924 return val !== undefined ? val : defaultVal;
42926 _addStorage: function _addStorage(storage) {
42927 if (this.enabled) {
42931 if (this._testStorage(storage)) {
42932 this.storage = storage;
42933 this.enabled = true;
42936 _addPlugin: function _addPlugin(plugin) {
42937 var self = this; // If the plugin is an array, then add all plugins in the array.
42938 // This allows for a plugin to depend on other plugins.
42940 if (isList$1(plugin)) {
42941 each$1(plugin, function (plugin) {
42942 self._addPlugin(plugin);
42945 } // Keep track of all plugins we've seen so far, so that we
42946 // don't add any of them twice.
42949 var seenPlugin = pluck$1(this.plugins, function (seenPlugin) {
42950 return plugin === seenPlugin;
42957 this.plugins.push(plugin); // Check that the plugin is properly formed
42959 if (!isFunction$1(plugin)) {
42960 throw new Error('Plugins must be function values that return objects');
42963 var pluginProperties = plugin.call(this);
42965 if (!isObject$3(pluginProperties)) {
42966 throw new Error('Plugins must return an object of function properties');
42967 } // Add the plugin function properties to this store instance.
42970 each$1(pluginProperties, function (pluginFnProp, propName) {
42971 if (!isFunction$1(pluginFnProp)) {
42972 throw new Error('Bad plugin property: ' + propName + ' from plugin ' + plugin.name + '. Plugins should only return functions.');
42975 self._assignPluginFnProp(pluginFnProp, propName);
42978 // Put deprecated properties in the private API, so as to not expose it to accidential
42979 // discovery through inspection of the store object.
42980 // Deprecated: addStorage
42981 addStorage: function addStorage(storage) {
42982 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
42984 this._addStorage(storage);
42987 var store = create$2(_privateStoreProps, storeAPI, {
42991 each$1(store, function (prop, propName) {
42992 if (isFunction$1(prop)) {
42993 store.raw[propName] = bind$2(store, prop);
42996 each$1(storages, function (storage) {
42997 store._addStorage(storage);
42999 each$1(plugins, function (plugin) {
43000 store._addPlugin(plugin);
43005 var Global$1 = util.Global;
43006 var localStorage_1 = {
43007 name: 'localStorage',
43015 function localStorage$1() {
43016 return Global$1.localStorage;
43019 function read(key) {
43020 return localStorage$1().getItem(key);
43023 function write(key, data) {
43024 return localStorage$1().setItem(key, data);
43027 function each$2(fn) {
43028 for (var i = localStorage$1().length - 1; i >= 0; i--) {
43029 var key = localStorage$1().key(i);
43030 fn(read(key), key);
43034 function remove$2(key) {
43035 return localStorage$1().removeItem(key);
43038 function clearAll() {
43039 return localStorage$1().clear();
43042 // versions 6 and 7, where no localStorage, etc
43045 var Global$2 = util.Global;
43046 var oldFFGlobalStorage = {
43047 name: 'oldFF-globalStorage',
43052 clearAll: clearAll$1
43054 var globalStorage = Global$2.globalStorage;
43056 function read$1(key) {
43057 return globalStorage[key];
43060 function write$1(key, data) {
43061 globalStorage[key] = data;
43064 function each$3(fn) {
43065 for (var i = globalStorage.length - 1; i >= 0; i--) {
43066 var key = globalStorage.key(i);
43067 fn(globalStorage[key], key);
43071 function remove$3(key) {
43072 return globalStorage.removeItem(key);
43075 function clearAll$1() {
43076 each$3(function (key, _) {
43077 delete globalStorage[key];
43081 // versions 6 and 7, where no localStorage, sessionStorage, etc
43084 var Global$3 = util.Global;
43085 var oldIEUserDataStorage = {
43086 name: 'oldIE-userDataStorage',
43091 clearAll: clearAll$2
43093 var storageName = 'storejs';
43094 var doc = Global$3.document;
43096 var _withStorageEl = _makeIEStorageElFunction();
43098 var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
43100 function write$2(unfixedKey, data) {
43105 var fixedKey = fixKey(unfixedKey);
43107 _withStorageEl(function (storageEl) {
43108 storageEl.setAttribute(fixedKey, data);
43109 storageEl.save(storageName);
43113 function read$2(unfixedKey) {
43118 var fixedKey = fixKey(unfixedKey);
43121 _withStorageEl(function (storageEl) {
43122 res = storageEl.getAttribute(fixedKey);
43128 function each$4(callback) {
43129 _withStorageEl(function (storageEl) {
43130 var attributes = storageEl.XMLDocument.documentElement.attributes;
43132 for (var i = attributes.length - 1; i >= 0; i--) {
43133 var attr = attributes[i];
43134 callback(storageEl.getAttribute(attr.name), attr.name);
43139 function remove$4(unfixedKey) {
43140 var fixedKey = fixKey(unfixedKey);
43142 _withStorageEl(function (storageEl) {
43143 storageEl.removeAttribute(fixedKey);
43144 storageEl.save(storageName);
43148 function clearAll$2() {
43149 _withStorageEl(function (storageEl) {
43150 var attributes = storageEl.XMLDocument.documentElement.attributes;
43151 storageEl.load(storageName);
43153 for (var i = attributes.length - 1; i >= 0; i--) {
43154 storageEl.removeAttribute(attributes[i].name);
43157 storageEl.save(storageName);
43161 // In IE7, keys cannot start with a digit or contain certain chars.
43162 // See https://github.com/marcuswestin/store.js/issues/40
43163 // See https://github.com/marcuswestin/store.js/issues/83
43166 var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
43168 function fixKey(key) {
43169 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___');
43172 function _makeIEStorageElFunction() {
43173 if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
43177 var scriptTag = 'script',
43180 storageEl; // Since #userData storage applies only to specific paths, we need to
43181 // somehow link our data to a specific path. We choose /favicon.ico
43182 // as a pretty safe option, since all browsers already make a request to
43183 // this URL anyway and being a 404 will not hurt us here. We wrap an
43184 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
43185 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
43186 // since the iframe access rules appear to allow direct access and
43187 // manipulation of the document element, even for a 404 page. This
43188 // document can be used instead of the current document (which would
43189 // have been limited to the current path) to perform #userData storage.
43192 /* global ActiveXObject */
43193 storageContainer = new ActiveXObject('htmlfile');
43194 storageContainer.open();
43195 storageContainer.write('<' + scriptTag + '>document.w=window</' + scriptTag + '><iframe src="/favicon.ico"></iframe>');
43196 storageContainer.close();
43197 storageOwner = storageContainer.w.frames[0].document;
43198 storageEl = storageOwner.createElement('div');
43200 // somehow ActiveXObject instantiation failed (perhaps some special
43201 // security settings or otherwse), fall back to per-path storage
43202 storageEl = doc.createElement('div');
43203 storageOwner = doc.body;
43206 return function (storeFunction) {
43207 var args = [].slice.call(arguments, 0);
43208 args.unshift(storageEl); // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
43209 // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
43211 storageOwner.appendChild(storageEl);
43212 storageEl.addBehavior('#default#userData');
43213 storageEl.load(storageName);
43214 storeFunction.apply(this, args);
43215 storageOwner.removeChild(storageEl);
43220 // doesn't work but cookies do. This implementation is adopted from
43221 // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
43223 var Global$4 = util.Global;
43224 var trim$4 = util.trim;
43225 var cookieStorage = {
43226 name: 'cookieStorage',
43231 clearAll: clearAll$3
43233 var doc$1 = Global$4.document;
43235 function read$3(key) {
43236 if (!key || !_has(key)) {
43240 var regexpStr = "(?:^|.*;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
43241 return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"));
43244 function each$5(callback) {
43245 var cookies = doc$1.cookie.split(/; ?/g);
43247 for (var i = cookies.length - 1; i >= 0; i--) {
43248 if (!trim$4(cookies[i])) {
43252 var kvp = cookies[i].split('=');
43253 var key = unescape(kvp[0]);
43254 var val = unescape(kvp[1]);
43255 callback(val, key);
43259 function write$3(key, data) {
43264 doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
43267 function remove$5(key) {
43268 if (!key || !_has(key)) {
43272 doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
43275 function clearAll$3() {
43276 each$5(function (_, key) {
43281 function _has(key) {
43282 return new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=").test(doc$1.cookie);
43285 var Global$5 = util.Global;
43286 var sessionStorage_1 = {
43287 name: 'sessionStorage',
43292 clearAll: clearAll$4
43295 function sessionStorage() {
43296 return Global$5.sessionStorage;
43299 function read$4(key) {
43300 return sessionStorage().getItem(key);
43303 function write$4(key, data) {
43304 return sessionStorage().setItem(key, data);
43307 function each$6(fn) {
43308 for (var i = sessionStorage().length - 1; i >= 0; i--) {
43309 var key = sessionStorage().key(i);
43310 fn(read$4(key), key);
43314 function remove$6(key) {
43315 return sessionStorage().removeItem(key);
43318 function clearAll$4() {
43319 return sessionStorage().clear();
43322 // memoryStorage is a useful last fallback to ensure that the store
43323 // is functions (meaning store.get(), store.set(), etc will all function).
43324 // However, stored values will not persist when the browser navigates to
43325 // a new page or reloads the current page.
43326 var memoryStorage_1 = {
43327 name: 'memoryStorage',
43332 clearAll: clearAll$5
43334 var memoryStorage = {};
43336 function read$5(key) {
43337 return memoryStorage[key];
43340 function write$5(key, data) {
43341 memoryStorage[key] = data;
43344 function each$7(callback) {
43345 for (var key in memoryStorage) {
43346 if (memoryStorage.hasOwnProperty(key)) {
43347 callback(memoryStorage[key], key);
43352 function remove$7(key) {
43353 delete memoryStorage[key];
43356 function clearAll$5(key) {
43357 memoryStorage = {};
43360 var all = [// Listed in order of usage preference
43361 localStorage_1, oldFFGlobalStorage, oldIEUserDataStorage, cookieStorage, sessionStorage_1, memoryStorage_1];
43363 /* eslint-disable */
43367 // NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
43368 // See http://www.JSON.org/js.html
43369 // This code should be minified before deployment.
43370 // See http://javascript.crockford.com/jsmin.html
43371 // USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
43373 // This file creates a global JSON object containing two methods: stringify
43374 // and parse. This file provides the ES5 JSON capability to ES3 systems.
43375 // If a project might run on IE8 or earlier, then this file should be included.
43376 // This file does nothing on ES5 systems.
43377 // JSON.stringify(value, replacer, space)
43378 // value any JavaScript value, usually an object or array.
43379 // replacer an optional parameter that determines how object
43380 // values are stringified for objects. It can be a
43381 // function or an array of strings.
43382 // space an optional parameter that specifies the indentation
43383 // of nested structures. If it is omitted, the text will
43384 // be packed without extra whitespace. If it is a number,
43385 // it will specify the number of spaces to indent at each
43386 // level. If it is a string (such as "\t" or " "),
43387 // it contains the characters used to indent at each level.
43388 // This method produces a JSON text from a JavaScript value.
43389 // When an object value is found, if the object contains a toJSON
43390 // method, its toJSON method will be called and the result will be
43391 // stringified. A toJSON method does not serialize: it returns the
43392 // value represented by the name/value pair that should be serialized,
43393 // or undefined if nothing should be serialized. The toJSON method
43394 // will be passed the key associated with the value, and this will be
43395 // bound to the value.
43396 // For example, this would serialize Dates as ISO strings.
43397 // Date.prototype.toJSON = function (key) {
43399 // // Format integers to have at least two digits.
43404 // return this.getUTCFullYear() + "-" +
43405 // f(this.getUTCMonth() + 1) + "-" +
43406 // f(this.getUTCDate()) + "T" +
43407 // f(this.getUTCHours()) + ":" +
43408 // f(this.getUTCMinutes()) + ":" +
43409 // f(this.getUTCSeconds()) + "Z";
43411 // You can provide an optional replacer method. It will be passed the
43412 // key and value of each member, with this bound to the containing
43413 // object. The value that is returned from your method will be
43414 // serialized. If your method returns undefined, then the member will
43415 // be excluded from the serialization.
43416 // If the replacer parameter is an array of strings, then it will be
43417 // used to select the members to be serialized. It filters the results
43418 // such that only members with keys listed in the replacer array are
43420 // Values that do not have JSON representations, such as undefined or
43421 // functions, will not be serialized. Such values in objects will be
43422 // dropped; in arrays they will be replaced with null. You can use
43423 // a replacer function to replace those with JSON values.
43424 // JSON.stringify(undefined) returns undefined.
43425 // The optional space parameter produces a stringification of the
43426 // value that is filled with line breaks and indentation to make it
43428 // If the space parameter is a non-empty string, then that string will
43429 // be used for indentation. If the space parameter is a number, then
43430 // the indentation will be that many spaces.
43432 // text = JSON.stringify(["e", {pluribus: "unum"}]);
43433 // // text is '["e",{"pluribus":"unum"}]'
43434 // text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
43435 // // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
43436 // text = JSON.stringify([new Date()], function (key, value) {
43437 // return this[key] instanceof Date
43438 // ? "Date(" + this[key] + ")"
43441 // // text is '["Date(---current time---)"]'
43442 // JSON.parse(text, reviver)
43443 // This method parses a JSON text to produce an object or array.
43444 // It can throw a SyntaxError exception.
43445 // The optional reviver parameter is a function that can filter and
43446 // transform the results. It receives each of the keys and values,
43447 // and its return value is used instead of the original value.
43448 // If it returns what it received, then the structure is not modified.
43449 // If it returns undefined then the member is deleted.
43451 // // Parse the text. Values that look like ISO date strings will
43452 // // be converted to Date objects.
43453 // myData = JSON.parse(text, function (key, value) {
43455 // if (typeof value === "string") {
43457 // /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
43459 // return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
43465 // myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
43467 // if (typeof value === "string" &&
43468 // value.slice(0, 5) === "Date(" &&
43469 // value.slice(-1) === ")") {
43470 // d = new Date(value.slice(5, -1));
43477 // This is a reference implementation. You are free to copy, modify, or
43485 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
43486 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
43487 lastIndex, length, parse, prototype, push, replace, slice, stringify,
43488 test, toJSON, toString, valueOf
43490 // Create a JSON object only if one does not already exist. We create the
43491 // methods in a closure to avoid creating global variables.
43492 if ((typeof JSON === "undefined" ? "undefined" : _typeof(JSON)) !== "object") {
43498 var rx_one = /^[\],:{}\s]*$/;
43499 var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
43500 var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
43501 var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
43502 var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
43503 var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
43506 // Format integers to have at least two digits.
43507 return n < 10 ? "0" + n : n;
43510 function this_value() {
43511 return this.valueOf();
43514 if (typeof Date.prototype.toJSON !== "function") {
43515 Date.prototype.toJSON = function () {
43516 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;
43519 Boolean.prototype.toJSON = this_value;
43520 Number.prototype.toJSON = this_value;
43521 String.prototype.toJSON = this_value;
43529 function quote(string) {
43530 // If the string contains no control characters, no quote characters, and no
43531 // backslash characters, then we can safely slap some quotes around it.
43532 // Otherwise we must also replace the offending characters with safe escape
43534 rx_escapable.lastIndex = 0;
43535 return rx_escapable.test(string) ? "\"" + string.replace(rx_escapable, function (a) {
43537 return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
43538 }) + "\"" : "\"" + string + "\"";
43541 function str(key, holder) {
43542 // Produce a string from holder[key].
43543 var i; // The loop counter.
43545 var k; // The member key.
43547 var v; // The member value.
43552 var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value.
43554 if (value && _typeof(value) === "object" && typeof value.toJSON === "function") {
43555 value = value.toJSON(key);
43556 } // If we were called with a replacer function, then call the replacer to
43557 // obtain a replacement value.
43560 if (typeof rep === "function") {
43561 value = rep.call(holder, key, value);
43562 } // What happens next depends on the value's type.
43565 switch (_typeof(value)) {
43567 return quote(value);
43570 // JSON numbers must be finite. Encode non-finite numbers as null.
43571 return isFinite(value) ? String(value) : "null";
43575 // If the value is a boolean or null, convert it to a string. Note:
43576 // typeof null does not produce "null". The case is included here in
43577 // the remote chance that this gets fixed someday.
43578 return String(value);
43579 // If the type is "object", we might be dealing with an object or an array or
43583 // Due to a specification blunder in ECMAScript, typeof null is "object",
43584 // so watch out for that case.
43587 } // Make an array to hold the partial results of stringifying this object value.
43591 partial = []; // Is the value an array?
43593 if (Object.prototype.toString.apply(value) === "[object Array]") {
43594 // The value is an array. Stringify every element. Use null as a placeholder
43595 // for non-JSON values.
43596 length = value.length;
43598 for (i = 0; i < length; i += 1) {
43599 partial[i] = str(i, value) || "null";
43600 } // Join all of the elements together, separated with commas, and wrap them in
43604 v = partial.length === 0 ? "[]" : gap ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : "[" + partial.join(",") + "]";
43607 } // If the replacer is an array, use it to select the members to be stringified.
43610 if (rep && _typeof(rep) === "object") {
43611 length = rep.length;
43613 for (i = 0; i < length; i += 1) {
43614 if (typeof rep[i] === "string") {
43619 partial.push(quote(k) + (gap ? ": " : ":") + v);
43624 // Otherwise, iterate through all of the keys in the object.
43626 if (Object.prototype.hasOwnProperty.call(value, k)) {
43630 partial.push(quote(k) + (gap ? ": " : ":") + v);
43634 } // Join all of the member texts together, separated with commas,
43635 // and wrap them in braces.
43638 v = partial.length === 0 ? "{}" : gap ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : "{" + partial.join(",") + "}";
43642 } // If the JSON object does not yet have a stringify method, give it one.
43645 if (typeof JSON.stringify !== "function") {
43647 // table of character substitutions
43657 JSON.stringify = function (value, replacer, space) {
43658 // The stringify method takes a value and an optional replacer, and an optional
43659 // space parameter, and returns a JSON text. The replacer can be a function
43660 // that can replace values, or an array of strings that will select the keys.
43661 // A default replacer method can be provided. Use of the space parameter can
43662 // produce text that is more easily readable.
43665 indent = ""; // If the space parameter is a number, make an indent string containing that
43668 if (typeof space === "number") {
43669 for (i = 0; i < space; i += 1) {
43671 } // If the space parameter is a string, it will be used as the indent string.
43673 } else if (typeof space === "string") {
43675 } // If there is a replacer, it must be a function or an array.
43676 // Otherwise, throw an error.
43681 if (replacer && typeof replacer !== "function" && (_typeof(replacer) !== "object" || typeof replacer.length !== "number")) {
43682 throw new Error("JSON.stringify");
43683 } // Make a fake root object containing our value under the key of "".
43684 // Return the result of stringifying the value.
43691 } // If the JSON object does not yet have a parse method, give it one.
43694 if (typeof JSON.parse !== "function") {
43695 JSON.parse = function (text, reviver) {
43696 // The parse method takes a text and an optional reviver function, and returns
43697 // a JavaScript value if the text is a valid JSON text.
43700 function walk(holder, key) {
43701 // The walk method is used to recursively walk the resulting structure so
43702 // that modifications can be made.
43705 var value = holder[key];
43707 if (value && _typeof(value) === "object") {
43709 if (Object.prototype.hasOwnProperty.call(value, k)) {
43710 v = walk(value, k);
43712 if (v !== undefined) {
43721 return reviver.call(holder, key, value);
43722 } // Parsing happens in four stages. In the first stage, we replace certain
43723 // Unicode characters with escape sequences. JavaScript handles many characters
43724 // incorrectly, either silently deleting them, or treating them as line endings.
43727 text = String(text);
43728 rx_dangerous.lastIndex = 0;
43730 if (rx_dangerous.test(text)) {
43731 text = text.replace(rx_dangerous, function (a) {
43732 return "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
43734 } // In the second stage, we run the text against regular expressions that look
43735 // for non-JSON patterns. We are especially concerned with "()" and "new"
43736 // because they can cause invocation, and "=" because it can cause mutation.
43737 // But just to be safe, we want to reject all unexpected forms.
43738 // We split the second stage into 4 regexp operations in order to work around
43739 // crippling inefficiencies in IE's and Safari's regexp engines. First we
43740 // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
43741 // replace all simple value tokens with "]" characters. Third, we delete all
43742 // open brackets that follow a colon or comma or that begin the text. Finally,
43743 // we look to see that the remaining characters are only whitespace or "]" or
43744 // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
43747 if (rx_one.test(text.replace(rx_two, "@").replace(rx_three, "]").replace(rx_four, ""))) {
43748 // In the third stage we use the eval function to compile the text into a
43749 // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
43750 // in JavaScript: it can begin a block or an object literal. We wrap the text
43751 // in parens to eliminate the ambiguity.
43752 j = eval("(" + text + ")"); // In the optional fourth stage, we recursively walk the new structure, passing
43753 // each name/value pair to a reviver function for possible transformation.
43755 return typeof reviver === "function" ? walk({
43758 } // If the text is not JSON parseable, then a SyntaxError is thrown.
43761 throw new SyntaxError("JSON.parse");
43766 var json2 = json2Plugin;
43768 function json2Plugin() {
43772 var plugins = [json2];
43773 var store_legacy = storeEngine.createStore(all, plugins);
43776 // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
43777 // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
43778 // does not support custom headers, which this uses everywhere.
43781 var osmAuth = function osmAuth(o) {
43782 var oauth = {}; // authenticated users will also have a request token secret, but it's
43783 // not used in transactions with the server
43785 oauth.authenticated = function () {
43786 return !!(token('oauth_token') && token('oauth_token_secret'));
43789 oauth.logout = function () {
43790 token('oauth_token', '');
43791 token('oauth_token_secret', '');
43792 token('oauth_request_token_secret', '');
43794 }; // TODO: detect lack of click event
43797 oauth.authenticate = function (callback) {
43798 if (oauth.authenticated()) return callback();
43799 oauth.logout(); // ## Getting a request token
43801 var params = timenonce(getAuth(o)),
43802 url = o.url + '/oauth/request_token';
43803 params.oauth_signature = ohauth_1.signature(o.oauth_secret, '', ohauth_1.baseString('POST', url, params));
43805 if (!o.singlepage) {
43806 // Create a 600x550 popup window in the center of the screen
43809 settings = [['width', w], ['height', h], ['left', screen.width / 2 - w / 2], ['top', screen.height / 2 - h / 2]].map(function (x) {
43810 return x.join('=');
43812 popup = window.open('about:blank', 'oauth_window', settings);
43813 oauth.popupWindow = popup;
43816 var error = new Error('Popup was blocked');
43817 error.status = 'popup-blocked';
43820 } // Request a request token. When this is complete, the popup
43821 // window is redirected to OSM's authorization page.
43824 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
43827 function reqTokenDone(err, xhr) {
43829 if (err) return callback(err);
43830 var resp = ohauth_1.stringQs(xhr.response);
43831 token('oauth_request_token_secret', resp.oauth_token_secret);
43832 var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
43833 oauth_token: resp.oauth_token,
43834 oauth_callback: resolveUrl$1(o.landing)
43837 if (o.singlepage) {
43838 location.href = authorize_url;
43840 popup.location = authorize_url;
43842 } // Called by a function in a landing page, in the popup window. The
43843 // window closes itself.
43846 window.authComplete = function (token) {
43847 var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
43848 get_access_token(oauth_token.oauth_token);
43849 delete window.authComplete;
43850 }; // ## Getting an request token
43852 // At this point we have an `oauth_token`, brought in from a function
43853 // call on a landing page popup.
43856 function get_access_token(oauth_token) {
43857 var url = o.url + '/oauth/access_token',
43858 params = timenonce(getAuth(o)),
43859 request_token_secret = token('oauth_request_token_secret');
43860 params.oauth_token = oauth_token;
43861 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
43863 // The final token required for authentication. At this point
43864 // we have a `request token secret`
43866 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
43870 function accessTokenDone(err, xhr) {
43872 if (err) return callback(err);
43873 var access_token = ohauth_1.stringQs(xhr.response);
43874 token('oauth_token', access_token.oauth_token);
43875 token('oauth_token_secret', access_token.oauth_token_secret);
43876 callback(null, oauth);
43880 oauth.bringPopupWindowToFront = function () {
43881 var brougtPopupToFront = false;
43884 // This may cause a cross-origin error:
43885 // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
43886 if (oauth.popupWindow && !oauth.popupWindow.closed) {
43887 oauth.popupWindow.focus();
43888 brougtPopupToFront = true;
43890 } catch (err) {// Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
43893 return brougtPopupToFront;
43896 oauth.bootstrapToken = function (oauth_token, callback) {
43897 // ## Getting an request token
43898 // At this point we have an `oauth_token`, brought in from a function
43899 // call on a landing page popup.
43900 function get_access_token(oauth_token) {
43901 var url = o.url + '/oauth/access_token',
43902 params = timenonce(getAuth(o)),
43903 request_token_secret = token('oauth_request_token_secret');
43904 params.oauth_token = oauth_token;
43905 params.oauth_signature = ohauth_1.signature(o.oauth_secret, request_token_secret, ohauth_1.baseString('POST', url, params)); // ## Getting an access token
43906 // The final token required for authentication. At this point
43907 // we have a `request token secret`
43909 ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
43913 function accessTokenDone(err, xhr) {
43915 if (err) return callback(err);
43916 var access_token = ohauth_1.stringQs(xhr.response);
43917 token('oauth_token', access_token.oauth_token);
43918 token('oauth_token_secret', access_token.oauth_token_secret);
43919 callback(null, oauth);
43922 get_access_token(oauth_token);
43925 // A single XMLHttpRequest wrapper that does authenticated calls if the
43926 // user has logged in.
43929 oauth.xhr = function (options, callback) {
43930 if (!oauth.authenticated()) {
43932 return oauth.authenticate(run);
43934 callback('not authenticated', null);
43942 var params = timenonce(getAuth(o)),
43943 oauth_token_secret = token('oauth_token_secret'),
43944 url = options.prefix !== false ? o.url + options.path : options.path,
43945 url_parts = url.replace(/#.*$/, '').split('?', 2),
43946 base_url = url_parts[0],
43947 query = url_parts.length === 2 ? url_parts[1] : ''; // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
43949 if ((!options.options || !options.options.header || options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') && options.content) {
43950 params = immutable(params, ohauth_1.stringQs(options.content));
43953 params.oauth_token = token('oauth_token');
43954 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))));
43955 return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
43958 function done(err, xhr) {
43959 if (err) return callback(err);else if (xhr.responseXML) return callback(err, xhr.responseXML);else return callback(err, xhr.response);
43961 }; // pre-authorize this object, if we can just get a token and token_secret
43965 oauth.preauth = function (c) {
43967 if (c.oauth_token) token('oauth_token', c.oauth_token);
43968 if (c.oauth_token_secret) token('oauth_token_secret', c.oauth_token_secret);
43972 oauth.options = function (_) {
43973 if (!arguments.length) return o;
43975 o.url = o.url || 'https://www.openstreetmap.org';
43976 o.landing = o.landing || 'land.html';
43977 o.singlepage = o.singlepage || false; // Optional loading and loading-done functions for nice UI feedback.
43978 // by default, no-ops
43980 o.loading = o.loading || function () {};
43982 o.done = o.done || function () {};
43984 return oauth.preauth(o);
43985 }; // 'stamp' an authentication object from `getAuth()`
43986 // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
43990 function timenonce(o) {
43991 o.oauth_timestamp = ohauth_1.timestamp();
43992 o.oauth_nonce = ohauth_1.nonce();
43994 } // get/set tokens. These are prefixed with the base URL so that `osm-auth`
43995 // can be used with multiple APIs and the keys in `localStorage`
44001 if (store_legacy.enabled) {
44002 token = function token(x, y) {
44003 if (arguments.length === 1) return store_legacy.get(o.url + x);else if (arguments.length === 2) return store_legacy.set(o.url + x, y);
44008 token = function token(x, y) {
44009 if (arguments.length === 1) return storage[o.url + x];else if (arguments.length === 2) return storage[o.url + x] = y;
44011 } // Get an authentication object. If you just add and remove properties
44012 // from a single object, you'll need to use `delete` to make sure that
44013 // it doesn't contain undesired properties for authentication
44016 function getAuth(o) {
44018 oauth_consumer_key: o.oauth_consumer_key,
44019 oauth_signature_method: 'HMAC-SHA1'
44021 } // potentially pre-authorize
44028 var JXON = new function () {
44029 var sValueProp = 'keyValue',
44030 sAttributesProp = 'keyAttributes',
44033 /* you can customize these values */
44036 rIsBool = /^(?:true|false)$/i;
44038 function parseText(sValue) {
44039 if (rIsNull.test(sValue)) {
44043 if (rIsBool.test(sValue)) {
44044 return sValue.toLowerCase() === 'true';
44047 if (isFinite(sValue)) {
44048 return parseFloat(sValue);
44051 if (isFinite(Date.parse(sValue))) {
44052 return new Date(sValue);
44058 function EmptyTree() {}
44060 EmptyTree.prototype.toString = function () {
44064 EmptyTree.prototype.valueOf = function () {
44068 function objectify(vValue) {
44069 return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
44072 function createObjTree(oParentNode, nVerb, bFreeze, bNesteAttr) {
44073 var nLevelStart = aCache.length,
44074 bChildren = oParentNode.hasChildNodes(),
44075 bAttributes = oParentNode.hasAttributes(),
44076 bHighVerb = Boolean(nVerb & 2);
44080 sCollectedTxt = '',
44081 vResult = bHighVerb ? {} :
44082 /* put here the default value for empty nodes: */
44086 for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
44087 oNode = oParentNode.childNodes.item(nItem);
44089 if (oNode.nodeType === 4) {
44090 sCollectedTxt += oNode.nodeValue;
44092 /* nodeType is 'CDATASection' (4) */
44093 else if (oNode.nodeType === 3) {
44094 sCollectedTxt += oNode.nodeValue.trim();
44096 /* nodeType is 'Text' (3) */
44097 else if (oNode.nodeType === 1 && !oNode.prefix) {
44098 aCache.push(oNode);
44100 /* nodeType is 'Element' (1) */
44105 var nLevelEnd = aCache.length,
44106 vBuiltVal = parseText(sCollectedTxt);
44108 if (!bHighVerb && (bChildren || bAttributes)) {
44109 vResult = nVerb === 0 ? objectify(vBuiltVal) : {};
44112 for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
44113 sProp = aCache[nElId].nodeName.toLowerCase();
44114 vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
44116 if (vResult.hasOwnProperty(sProp)) {
44117 if (vResult[sProp].constructor !== Array) {
44118 vResult[sProp] = [vResult[sProp]];
44121 vResult[sProp].push(vContent);
44123 vResult[sProp] = vContent;
44129 var nAttrLen = oParentNode.attributes.length,
44130 sAPrefix = bNesteAttr ? '' : sAttrPref,
44131 oAttrParent = bNesteAttr ? {} : vResult;
44133 for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
44134 oAttrib = oParentNode.attributes.item(nAttrib);
44135 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
44140 Object.freeze(oAttrParent);
44143 vResult[sAttributesProp] = oAttrParent;
44144 nLength -= nAttrLen - 1;
44148 if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
44149 vResult[sValueProp] = vBuiltVal;
44150 } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
44151 vResult = vBuiltVal;
44154 if (bFreeze && (bHighVerb || nLength > 0)) {
44155 Object.freeze(vResult);
44158 aCache.length = nLevelStart;
44162 function loadObjTree(oXMLDoc, oParentEl, oParentObj) {
44163 var vValue, oChild;
44165 if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
44166 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString()));
44167 /* verbosity level is 0 */
44168 } else if (oParentObj.constructor === Date) {
44169 oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));
44172 for (var sName in oParentObj) {
44173 vValue = oParentObj[sName];
44175 if (isFinite(sName) || vValue instanceof Function) {
44178 /* verbosity level is 0 */
44181 if (sName === sValueProp) {
44182 if (vValue !== null && vValue !== true) {
44183 oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
44185 } else if (sName === sAttributesProp) {
44186 /* verbosity level is 3 */
44187 for (var sAttrib in vValue) {
44188 oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
44190 } else if (sName.charAt(0) === sAttrPref) {
44191 oParentEl.setAttribute(sName.slice(1), vValue);
44192 } else if (vValue.constructor === Array) {
44193 for (var nItem = 0; nItem < vValue.length; nItem++) {
44194 oChild = oXMLDoc.createElement(sName);
44195 loadObjTree(oXMLDoc, oChild, vValue[nItem]);
44196 oParentEl.appendChild(oChild);
44199 oChild = oXMLDoc.createElement(sName);
44201 if (vValue instanceof Object) {
44202 loadObjTree(oXMLDoc, oChild, vValue);
44203 } else if (vValue !== null && vValue !== true) {
44204 oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
44207 oParentEl.appendChild(oChild);
44212 this.build = function (oXMLParent, nVerbosity
44219 var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 :
44220 /* put here the default verbosity level: */
44223 return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);
44226 this.unbuild = function (oObjTree) {
44227 var oNewDoc = document.implementation.createDocument('', '', null);
44228 loadObjTree(oNewDoc, oNewDoc, oObjTree);
44232 this.stringify = function (oObjTree) {
44233 return new XMLSerializer().serializeToString(JXON.unbuild(oObjTree));
44235 }(); // var myObject = JXON.build(doc);
44236 // we got our javascript object! try: alert(JSON.stringify(myObject));
44237 // var newDoc = JXON.unbuild(myObject);
44238 // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
44240 var tiler$5 = utilTiler();
44241 var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
44242 var urlroot = 'https://www.openstreetmap.org';
44243 var oauth = osmAuth({
44245 oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
44246 oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
44247 loading: authLoading,
44249 }); // hardcode default block of Google Maps
44251 var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
44273 var _cachedApiStatus;
44275 var _changeset = {};
44277 var _deferred = new Set();
44279 var _connectionID = 1;
44280 var _tileZoom$3 = 16;
44281 var _noteZoom = 12;
44283 var _rateLimitError;
44285 var _userChangesets;
44289 var _off; // set a default but also load this from the API status
44292 var _maxWayNodes = 2000;
44294 function authLoading() {
44295 dispatch$6.call('authLoading');
44298 function authDone() {
44299 dispatch$6.call('authDone');
44302 function abortRequest$5(controllerOrXHR) {
44303 if (controllerOrXHR) {
44304 controllerOrXHR.abort();
44308 function hasInflightRequests(cache) {
44309 return Object.keys(cache.inflight).length;
44312 function abortUnwantedRequests$3(cache, visibleTiles) {
44313 Object.keys(cache.inflight).forEach(function (k) {
44314 if (cache.toLoad[k]) return;
44315 if (visibleTiles.find(function (tile) {
44316 return k === tile.id;
44318 abortRequest$5(cache.inflight[k]);
44319 delete cache.inflight[k];
44323 function getLoc(attrs) {
44324 var lon = attrs.lon && attrs.lon.value;
44325 var lat = attrs.lat && attrs.lat.value;
44326 return [parseFloat(lon), parseFloat(lat)];
44329 function getNodes(obj) {
44330 var elems = obj.getElementsByTagName('nd');
44331 var nodes = new Array(elems.length);
44333 for (var i = 0, l = elems.length; i < l; i++) {
44334 nodes[i] = 'n' + elems[i].attributes.ref.value;
44340 function getNodesJSON(obj) {
44341 var elems = obj.nodes;
44342 var nodes = new Array(elems.length);
44344 for (var i = 0, l = elems.length; i < l; i++) {
44345 nodes[i] = 'n' + elems[i];
44351 function getTags(obj) {
44352 var elems = obj.getElementsByTagName('tag');
44355 for (var i = 0, l = elems.length; i < l; i++) {
44356 var attrs = elems[i].attributes;
44357 tags[attrs.k.value] = attrs.v.value;
44363 function getMembers(obj) {
44364 var elems = obj.getElementsByTagName('member');
44365 var members = new Array(elems.length);
44367 for (var i = 0, l = elems.length; i < l; i++) {
44368 var attrs = elems[i].attributes;
44370 id: attrs.type.value[0] + attrs.ref.value,
44371 type: attrs.type.value,
44372 role: attrs.role.value
44379 function getMembersJSON(obj) {
44380 var elems = obj.members;
44381 var members = new Array(elems.length);
44383 for (var i = 0, l = elems.length; i < l; i++) {
44384 var attrs = elems[i];
44386 id: attrs.type[0] + attrs.ref,
44395 function getVisible(attrs) {
44396 return !attrs.visible || attrs.visible.value !== 'false';
44399 function parseComments(comments) {
44400 var parsedComments = []; // for each comment
44402 for (var i = 0; i < comments.length; i++) {
44403 var comment = comments[i];
44405 if (comment.nodeName === 'comment') {
44406 var childNodes = comment.childNodes;
44407 var parsedComment = {};
44409 for (var j = 0; j < childNodes.length; j++) {
44410 var node = childNodes[j];
44411 var nodeName = node.nodeName;
44412 if (nodeName === '#text') continue;
44413 parsedComment[nodeName] = node.textContent;
44415 if (nodeName === 'uid') {
44416 var uid = node.textContent;
44418 if (uid && !_userCache.user[uid]) {
44419 _userCache.toLoad[uid] = true;
44424 if (parsedComment) {
44425 parsedComments.push(parsedComment);
44430 return parsedComments;
44433 function encodeNoteRtree(note) {
44443 var jsonparsers = {
44444 node: function nodeData(obj, uid) {
44445 return new osmNode({
44447 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44448 version: obj.version && obj.version.toString(),
44449 changeset: obj.changeset && obj.changeset.toString(),
44450 timestamp: obj.timestamp,
44452 uid: obj.uid && obj.uid.toString(),
44453 loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
44457 way: function wayData(obj, uid) {
44458 return new osmWay({
44460 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44461 version: obj.version && obj.version.toString(),
44462 changeset: obj.changeset && obj.changeset.toString(),
44463 timestamp: obj.timestamp,
44465 uid: obj.uid && obj.uid.toString(),
44467 nodes: getNodesJSON(obj)
44470 relation: function relationData(obj, uid) {
44471 return new osmRelation({
44473 visible: typeof obj.visible === 'boolean' ? obj.visible : true,
44474 version: obj.version && obj.version.toString(),
44475 changeset: obj.changeset && obj.changeset.toString(),
44476 timestamp: obj.timestamp,
44478 uid: obj.uid && obj.uid.toString(),
44480 members: getMembersJSON(obj)
44485 function parseJSON(payload, callback, options) {
44486 options = Object.assign({
44492 message: 'No JSON',
44497 var json = payload;
44498 if (_typeof(json) !== 'object') json = JSON.parse(payload);
44499 if (!json.elements) return callback({
44500 message: 'No JSON',
44503 var children = json.elements;
44504 var handle = window.requestIdleCallback(function () {
44508 for (var i = 0; i < children.length; i++) {
44509 result = parseChild(children[i]);
44510 if (result) results.push(result);
44513 callback(null, results);
44516 _deferred.add(handle);
44518 function parseChild(child) {
44519 var parser = jsonparsers[child.type];
44520 if (!parser) return null;
44522 uid = osmEntity.id.fromOSM(child.type, child.id);
44524 if (options.skipSeen) {
44525 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
44527 _tileCache.seen[uid] = true;
44530 return parser(child, uid);
44535 node: function nodeData(obj, uid) {
44536 var attrs = obj.attributes;
44537 return new osmNode({
44539 visible: getVisible(attrs),
44540 version: attrs.version.value,
44541 changeset: attrs.changeset && attrs.changeset.value,
44542 timestamp: attrs.timestamp && attrs.timestamp.value,
44543 user: attrs.user && attrs.user.value,
44544 uid: attrs.uid && attrs.uid.value,
44545 loc: getLoc(attrs),
44549 way: function wayData(obj, uid) {
44550 var attrs = obj.attributes;
44551 return new osmWay({
44553 visible: getVisible(attrs),
44554 version: attrs.version.value,
44555 changeset: attrs.changeset && attrs.changeset.value,
44556 timestamp: attrs.timestamp && attrs.timestamp.value,
44557 user: attrs.user && attrs.user.value,
44558 uid: attrs.uid && attrs.uid.value,
44559 tags: getTags(obj),
44560 nodes: getNodes(obj)
44563 relation: function relationData(obj, uid) {
44564 var attrs = obj.attributes;
44565 return new osmRelation({
44567 visible: getVisible(attrs),
44568 version: attrs.version.value,
44569 changeset: attrs.changeset && attrs.changeset.value,
44570 timestamp: attrs.timestamp && attrs.timestamp.value,
44571 user: attrs.user && attrs.user.value,
44572 uid: attrs.uid && attrs.uid.value,
44573 tags: getTags(obj),
44574 members: getMembers(obj)
44577 note: function parseNote(obj, uid) {
44578 var attrs = obj.attributes;
44579 var childNodes = obj.childNodes;
44582 props.loc = getLoc(attrs); // if notes are coincident, move them apart slightly
44584 var coincident = false;
44585 var epsilon = 0.00001;
44589 props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
44592 var bbox = geoExtent(props.loc).bbox();
44593 coincident = _noteCache.rtree.search(bbox).length;
44594 } while (coincident); // parse note contents
44597 for (var i = 0; i < childNodes.length; i++) {
44598 var node = childNodes[i];
44599 var nodeName = node.nodeName;
44600 if (nodeName === '#text') continue; // if the element is comments, parse the comments
44602 if (nodeName === 'comments') {
44603 props[nodeName] = parseComments(node.childNodes);
44605 props[nodeName] = node.textContent;
44609 var note = new osmNote(props);
44610 var item = encodeNoteRtree(note);
44611 _noteCache.note[note.id] = note;
44613 _noteCache.rtree.insert(item);
44617 user: function parseUser(obj, uid) {
44618 var attrs = obj.attributes;
44621 display_name: attrs.display_name && attrs.display_name.value,
44622 account_created: attrs.account_created && attrs.account_created.value,
44623 changesets_count: '0',
44626 var img = obj.getElementsByTagName('img');
44628 if (img && img[0] && img[0].getAttribute('href')) {
44629 user.image_url = img[0].getAttribute('href');
44632 var changesets = obj.getElementsByTagName('changesets');
44634 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
44635 user.changesets_count = changesets[0].getAttribute('count');
44638 var blocks = obj.getElementsByTagName('blocks');
44640 if (blocks && blocks[0]) {
44641 var received = blocks[0].getElementsByTagName('received');
44643 if (received && received[0] && received[0].getAttribute('active')) {
44644 user.active_blocks = received[0].getAttribute('active');
44648 _userCache.user[uid] = user;
44649 delete _userCache.toLoad[uid];
44654 function parseXML(xml, callback, options) {
44655 options = Object.assign({
44659 if (!xml || !xml.childNodes) {
44666 var root = xml.childNodes[0];
44667 var children = root.childNodes;
44668 var handle = window.requestIdleCallback(function () {
44672 for (var i = 0; i < children.length; i++) {
44673 result = parseChild(children[i]);
44674 if (result) results.push(result);
44677 callback(null, results);
44680 _deferred.add(handle);
44682 function parseChild(child) {
44683 var parser = parsers[child.nodeName];
44684 if (!parser) return null;
44687 if (child.nodeName === 'user') {
44688 uid = child.attributes.id.value;
44690 if (options.skipSeen && _userCache.user[uid]) {
44691 delete _userCache.toLoad[uid];
44694 } else if (child.nodeName === 'note') {
44695 uid = child.getElementsByTagName('id')[0].textContent;
44697 uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
44699 if (options.skipSeen) {
44700 if (_tileCache.seen[uid]) return null; // avoid reparsing a "seen" entity
44702 _tileCache.seen[uid] = true;
44706 return parser(child, uid);
44708 } // replace or remove note from rtree
44711 function updateRtree$3(item, replace) {
44712 _noteCache.rtree.remove(item, function isEql(a, b) {
44713 return a.data.id === b.data.id;
44717 _noteCache.rtree.insert(item);
44721 function wrapcb(thisArg, callback, cid) {
44722 return function (err, result) {
44724 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
44725 if (err.status === 400 || err.status === 401 || err.status === 403) {
44729 return callback.call(thisArg, err);
44730 } else if (thisArg.getConnectionId() !== cid) {
44731 return callback.call(thisArg, {
44732 message: 'Connection Switched',
44736 return callback.call(thisArg, err, result);
44742 init: function init() {
44743 utilRebind(this, dispatch$6, 'on');
44745 reset: function reset() {
44746 Array.from(_deferred).forEach(function (handle) {
44747 window.cancelIdleCallback(handle);
44749 _deferred["delete"](handle);
44752 _userChangesets = undefined;
44753 _userDetails = undefined;
44754 _rateLimitError = undefined;
44755 Object.values(_tileCache.inflight).forEach(abortRequest$5);
44756 Object.values(_noteCache.inflight).forEach(abortRequest$5);
44757 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
44758 if (_changeset.inflight) abortRequest$5(_changeset.inflight);
44779 _cachedApiStatus = undefined;
44783 getConnectionId: function getConnectionId() {
44784 return _connectionID;
44786 changesetURL: function changesetURL(changesetID) {
44787 return urlroot + '/changeset/' + changesetID;
44789 changesetsURL: function changesetsURL(center, zoom) {
44790 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
44791 return urlroot + '/history#map=' + Math.floor(zoom) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
44793 entityURL: function entityURL(entity) {
44794 return urlroot + '/' + entity.type + '/' + entity.osmId();
44796 historyURL: function historyURL(entity) {
44797 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
44799 userURL: function userURL(username) {
44800 return urlroot + '/user/' + username;
44802 noteURL: function noteURL(note) {
44803 return urlroot + '/note/' + note.id;
44805 noteReportURL: function noteReportURL(note) {
44806 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
44808 // Generic method to load data from the OSM API
44809 // Can handle either auth or unauth calls.
44810 loadFromAPI: function loadFromAPI(path, callback, options) {
44811 options = Object.assign({
44815 var cid = _connectionID;
44817 function done(err, payload) {
44818 if (that.getConnectionId() !== cid) {
44819 if (callback) callback({
44820 message: 'Connection Switched',
44826 var isAuthenticated = that.authenticated(); // 400 Bad Request, 401 Unauthorized, 403 Forbidden
44827 // Logout and retry the request..
44829 if (isAuthenticated && err && err.status && (err.status === 400 || err.status === 401 || err.status === 403)) {
44831 that.loadFromAPI(path, callback, options); // else, no retry..
44833 // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
44834 // Set the rateLimitError flag and trigger a warning..
44835 if (!isAuthenticated && !_rateLimitError && err && err.status && (err.status === 509 || err.status === 429)) {
44836 _rateLimitError = err;
44837 dispatch$6.call('change');
44838 that.reloadApiStatus();
44839 } else if (err && _cachedApiStatus === 'online' || !err && _cachedApiStatus !== 'online') {
44840 // If the response's error state doesn't match the status,
44841 // it's likely we lost or gained the connection so reload the status
44842 that.reloadApiStatus();
44847 return callback(err);
44849 if (path.indexOf('.json') !== -1) {
44850 return parseJSON(payload, callback, options);
44852 return parseXML(payload, callback, options);
44859 if (this.authenticated()) {
44865 var url = urlroot + path;
44866 var controller = new AbortController();
44868 signal: controller.signal
44869 }).then(function (data) {
44871 })["catch"](function (err) {
44872 if (err.name === 'AbortError') return; // d3-fetch includes status in the error message,
44873 // but we can't access the response itself
44874 // https://github.com/d3/d3-fetch/issues/27
44876 var match = err.message.match(/^\d{3}/);
44881 statusText: err.message
44890 // Load a single entity by id (ways and relations use the `/full` call)
44891 // GET /api/0.6/node/#id
44892 // GET /api/0.6/[way|relation]/#id/full
44893 loadEntity: function loadEntity(id, callback) {
44894 var type = osmEntity.id.type(id);
44895 var osmID = osmEntity.id.toOSM(id);
44899 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json', function (err, entities) {
44900 if (callback) callback(err, {
44905 // Load a single entity with a specific version
44906 // GET /api/0.6/[node|way|relation]/#id/#version
44907 loadEntityVersion: function loadEntityVersion(id, version, callback) {
44908 var type = osmEntity.id.type(id);
44909 var osmID = osmEntity.id.toOSM(id);
44913 this.loadFromAPI('/api/0.6/' + type + '/' + osmID + '/' + version + '.json', function (err, entities) {
44914 if (callback) callback(err, {
44919 // Load multiple entities in chunks
44920 // (note: callback may be called multiple times)
44921 // Unlike `loadEntity`, child nodes and members are not fetched
44922 // GET /api/0.6/[nodes|ways|relations]?#parameters
44923 loadMultiple: function loadMultiple(ids, callback) {
44925 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
44926 Object.keys(groups).forEach(function (k) {
44927 var type = k + 's'; // nodes, ways, relations
44929 var osmIDs = groups[k].map(function (id) {
44930 return osmEntity.id.toOSM(id);
44935 utilArrayChunk(osmIDs, 150).forEach(function (arr) {
44936 that.loadFromAPI('/api/0.6/' + type + '.json?' + type + '=' + arr.join(), function (err, entities) {
44937 if (callback) callback(err, {
44944 // Create, upload, and close a changeset
44945 // PUT /api/0.6/changeset/create
44946 // POST /api/0.6/changeset/#id/upload
44947 // PUT /api/0.6/changeset/#id/close
44948 putChangeset: function putChangeset(changeset, changes, callback) {
44949 var cid = _connectionID;
44951 if (_changeset.inflight) {
44953 message: 'Changeset already inflight',
44956 } else if (_changeset.open) {
44957 // reuse existing open changeset..
44958 return createdChangeset.call(this, null, _changeset.open);
44960 // Open a new changeset..
44963 path: '/api/0.6/changeset/create',
44966 'Content-Type': 'text/xml'
44969 content: JXON.stringify(changeset.asJXON())
44971 _changeset.inflight = oauth.xhr(options, wrapcb(this, createdChangeset, cid));
44974 function createdChangeset(err, changesetID) {
44975 _changeset.inflight = null;
44978 return callback(err, changeset);
44981 _changeset.open = changesetID;
44982 changeset = changeset.update({
44984 }); // Upload the changeset..
44988 path: '/api/0.6/changeset/' + changesetID + '/upload',
44991 'Content-Type': 'text/xml'
44994 content: JXON.stringify(changeset.osmChangeJXON(changes))
44996 _changeset.inflight = oauth.xhr(options, wrapcb(this, uploadedChangeset, cid));
44999 function uploadedChangeset(err) {
45000 _changeset.inflight = null;
45001 if (err) return callback(err, changeset); // Upload was successful, safe to call the callback.
45002 // Add delay to allow for postgres replication #1646 #2678
45004 window.setTimeout(function () {
45005 callback(null, changeset);
45007 _changeset.open = null; // At this point, we don't really care if the connection was switched..
45008 // Only try to close the changeset if we're still talking to the same server.
45010 if (this.getConnectionId() === cid) {
45011 // Still attempt to close changeset, but ignore response because #2667
45014 path: '/api/0.6/changeset/' + changeset.id + '/close',
45017 'Content-Type': 'text/xml'
45026 // Load multiple users in chunks
45027 // (note: callback may be called multiple times)
45028 // GET /api/0.6/users?users=#id1,#id2,...,#idn
45029 loadUsers: function loadUsers(uids, callback) {
45032 utilArrayUniq(uids).forEach(function (uid) {
45033 if (_userCache.user[uid]) {
45034 delete _userCache.toLoad[uid];
45035 cached.push(_userCache.user[uid]);
45041 if (cached.length || !this.authenticated()) {
45042 callback(undefined, cached);
45043 if (!this.authenticated()) return; // require auth
45046 utilArrayChunk(toLoad, 150).forEach(function (arr) {
45049 path: '/api/0.6/users?users=' + arr.join()
45050 }, wrapcb(this, done, _connectionID));
45053 function done(err, xml) {
45055 return callback(err);
45061 return parseXML(xml, function (err, results) {
45063 return callback(err);
45065 return callback(undefined, results);
45070 // Load a given user by id
45071 // GET /api/0.6/user/#id
45072 loadUser: function loadUser(uid, callback) {
45073 if (_userCache.user[uid] || !this.authenticated()) {
45075 delete _userCache.toLoad[uid];
45076 return callback(undefined, _userCache.user[uid]);
45081 path: '/api/0.6/user/' + uid
45082 }, wrapcb(this, done, _connectionID));
45084 function done(err, xml) {
45086 return callback(err);
45092 return parseXML(xml, function (err, results) {
45094 return callback(err);
45096 return callback(undefined, results[0]);
45101 // Load the details of the logged-in user
45102 // GET /api/0.6/user/details
45103 userDetails: function userDetails(callback) {
45104 if (_userDetails) {
45106 return callback(undefined, _userDetails);
45111 path: '/api/0.6/user/details'
45112 }, wrapcb(this, done, _connectionID));
45114 function done(err, xml) {
45116 return callback(err);
45122 return parseXML(xml, function (err, results) {
45124 return callback(err);
45126 _userDetails = results[0];
45127 return callback(undefined, _userDetails);
45132 // Load previous changesets for the logged in user
45133 // GET /api/0.6/changesets?user=#id
45134 userChangesets: function userChangesets(callback) {
45135 if (_userChangesets) {
45137 return callback(undefined, _userChangesets);
45140 this.userDetails(wrapcb(this, gotDetails, _connectionID));
45142 function gotDetails(err, user) {
45144 return callback(err);
45149 path: '/api/0.6/changesets?user=' + user.id
45150 }, wrapcb(this, done, _connectionID));
45153 function done(err, xml) {
45155 return callback(err);
45158 _userChangesets = Array.prototype.map.call(xml.getElementsByTagName('changeset'), function (changeset) {
45160 tags: getTags(changeset)
45162 }).filter(function (changeset) {
45163 var comment = changeset.tags.comment;
45164 return comment && comment !== '';
45166 return callback(undefined, _userChangesets);
45169 // Fetch the status of the OSM API
45170 // GET /api/capabilities
45171 status: function status(callback) {
45172 var url = urlroot + '/api/capabilities';
45173 var errback = wrapcb(this, done, _connectionID);
45174 d3_xml(url).then(function (data) {
45175 errback(null, data);
45176 })["catch"](function (err) {
45177 errback(err.message);
45180 function done(err, xml) {
45182 // the status is null if no response could be retrieved
45183 return callback(err, null);
45184 } // update blocklists
45187 var elements = xml.getElementsByTagName('blacklist');
45190 for (var i = 0; i < elements.length; i++) {
45191 var regexString = elements[i].getAttribute('regex'); // needs unencode?
45195 var regex = new RegExp(regexString);
45196 regexes.push(regex);
45203 if (regexes.length) {
45204 _imageryBlocklists = regexes;
45207 if (_rateLimitError) {
45208 return callback(_rateLimitError, 'rateLimited');
45210 var waynodes = xml.getElementsByTagName('waynodes');
45211 var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
45212 if (maxWayNodes && isFinite(maxWayNodes)) _maxWayNodes = maxWayNodes;
45213 var apiStatus = xml.getElementsByTagName('status');
45214 var val = apiStatus[0].getAttribute('api');
45215 return callback(undefined, val);
45219 // Calls `status` and dispatches an `apiStatusChange` event if the returned
45220 // status differs from the cached status.
45221 reloadApiStatus: function reloadApiStatus() {
45222 // throttle to avoid unnecessary API calls
45223 if (!this.throttledReloadApiStatus) {
45225 this.throttledReloadApiStatus = throttle(function () {
45226 that.status(function (err, status) {
45227 if (status !== _cachedApiStatus) {
45228 _cachedApiStatus = status;
45229 dispatch$6.call('apiStatusChange', that, err, status);
45235 this.throttledReloadApiStatus();
45237 // Returns the maximum number of nodes a single way can have
45238 maxWayNodes: function maxWayNodes() {
45239 return _maxWayNodes;
45241 // Load data (entities) from the API in tiles
45242 // GET /api/0.6/map?bbox=
45243 loadTiles: function loadTiles(projection, callback) {
45244 if (_off) return; // determine the needed tiles to cover the view
45246 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection); // abort inflight requests that are no longer needed
45248 var hadRequests = hasInflightRequests(_tileCache);
45249 abortUnwantedRequests$3(_tileCache, tiles);
45251 if (hadRequests && !hasInflightRequests(_tileCache)) {
45252 dispatch$6.call('loaded'); // stop the spinner
45253 } // issue new requests..
45256 tiles.forEach(function (tile) {
45257 this.loadTile(tile, callback);
45260 // Load a single data tile
45261 // GET /api/0.6/map?bbox=
45262 loadTile: function loadTile(tile, callback) {
45264 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
45266 if (!hasInflightRequests(_tileCache)) {
45267 dispatch$6.call('loading'); // start the spinner
45270 var path = '/api/0.6/map.json?bbox=';
45274 _tileCache.inflight[tile.id] = this.loadFromAPI(path + tile.extent.toParam(), tileCallback, options);
45276 function tileCallback(err, parsed) {
45277 delete _tileCache.inflight[tile.id];
45280 delete _tileCache.toLoad[tile.id];
45281 _tileCache.loaded[tile.id] = true;
45282 var bbox = tile.extent.bbox();
45285 _tileCache.rtree.insert(bbox);
45289 callback(err, Object.assign({
45294 if (!hasInflightRequests(_tileCache)) {
45295 dispatch$6.call('loaded'); // stop the spinner
45299 isDataLoaded: function isDataLoaded(loc) {
45306 return _tileCache.rtree.collides(bbox);
45308 // load the tile that covers the given `loc`
45309 loadTileAtLoc: function loadTileAtLoc(loc, callback) {
45310 // Back off if the toLoad queue is filling up.. re #6417
45311 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
45312 // let users safely edit geometries which extend to unloaded tiles. We can drop some.)
45313 if (Object.keys(_tileCache.toLoad).length > 50) return;
45314 var k = geoZoomToScale(_tileZoom$3 + 1);
45315 var offset = geoRawMercator().scale(k)(loc);
45316 var projection = geoRawMercator().transform({
45321 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
45322 tiles.forEach(function (tile) {
45323 if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) return;
45324 _tileCache.toLoad[tile.id] = true;
45325 this.loadTile(tile, callback);
45328 // Load notes from the API in tiles
45329 // GET /api/0.6/notes?bbox=
45330 loadNotes: function loadNotes(projection, noteOptions) {
45331 noteOptions = Object.assign({
45337 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
45339 var throttleLoadUsers = throttle(function () {
45340 var uids = Object.keys(_userCache.toLoad);
45341 if (!uids.length) return;
45342 that.loadUsers(uids, function () {}); // eagerly load user details
45343 }, 750); // determine the needed tiles to cover the view
45346 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection); // abort inflight requests that are no longer needed
45348 abortUnwantedRequests$3(_noteCache, tiles); // issue new requests..
45350 tiles.forEach(function (tile) {
45351 if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) return;
45355 _noteCache.inflight[tile.id] = that.loadFromAPI(path + tile.extent.toParam(), function (err) {
45356 delete _noteCache.inflight[tile.id];
45359 _noteCache.loaded[tile.id] = true;
45362 throttleLoadUsers();
45363 dispatch$6.call('loadedNotes');
45368 // POST /api/0.6/notes?params
45369 postNoteCreate: function postNoteCreate(note, callback) {
45370 if (!this.authenticated()) {
45372 message: 'Not Authenticated',
45377 if (_noteCache.inflightPost[note.id]) {
45379 message: 'Note update already inflight',
45384 if (!note.loc[0] || !note.loc[1] || !note.newComment) return; // location & description required
45386 var comment = note.newComment;
45388 if (note.newCategory && note.newCategory !== 'None') {
45389 comment += ' #' + note.newCategory;
45392 var path = '/api/0.6/notes?' + utilQsString({
45397 _noteCache.inflightPost[note.id] = oauth.xhr({
45400 }, wrapcb(this, done, _connectionID));
45402 function done(err, xml) {
45403 delete _noteCache.inflightPost[note.id];
45406 return callback(err);
45407 } // we get the updated note back, remove from caches and reparse..
45410 this.removeNote(note);
45414 return parseXML(xml, function (err, results) {
45416 return callback(err);
45418 return callback(undefined, results[0]);
45424 // POST /api/0.6/notes/#id/comment?text=comment
45425 // POST /api/0.6/notes/#id/close?text=comment
45426 // POST /api/0.6/notes/#id/reopen?text=comment
45427 postNoteUpdate: function postNoteUpdate(note, newStatus, callback) {
45428 if (!this.authenticated()) {
45430 message: 'Not Authenticated',
45435 if (_noteCache.inflightPost[note.id]) {
45437 message: 'Note update already inflight',
45444 if (note.status !== 'closed' && newStatus === 'closed') {
45446 } else if (note.status !== 'open' && newStatus === 'open') {
45449 action = 'comment';
45450 if (!note.newComment) return; // when commenting, comment required
45453 var path = '/api/0.6/notes/' + note.id + '/' + action;
45455 if (note.newComment) {
45456 path += '?' + utilQsString({
45457 text: note.newComment
45461 _noteCache.inflightPost[note.id] = oauth.xhr({
45464 }, wrapcb(this, done, _connectionID));
45466 function done(err, xml) {
45467 delete _noteCache.inflightPost[note.id];
45470 return callback(err);
45471 } // we get the updated note back, remove from caches and reparse..
45474 this.removeNote(note); // update closed note cache - used to populate `closed:note` changeset tag
45476 if (action === 'close') {
45477 _noteCache.closed[note.id] = true;
45478 } else if (action === 'reopen') {
45479 delete _noteCache.closed[note.id];
45485 return parseXML(xml, function (err, results) {
45487 return callback(err);
45489 return callback(undefined, results[0]);
45494 "switch": function _switch(options) {
45495 urlroot = options.urlroot;
45496 oauth.options(Object.assign({
45498 loading: authLoading,
45502 this.userChangesets(function () {}); // eagerly load user details/changesets
45504 dispatch$6.call('change');
45507 toggle: function toggle(val) {
45511 isChangesetInflight: function isChangesetInflight() {
45512 return !!_changeset.inflight;
45514 // get/set cached data
45515 // This is used to save/restore the state when entering/exiting the walkthrough
45516 // Also used for testing purposes.
45517 caches: function caches(obj) {
45518 function cloneCache(source) {
45520 Object.keys(source).forEach(function (k) {
45521 if (k === 'rtree') {
45522 target.rtree = new RBush().fromJSON(source.rtree.toJSON()); // clone rbush
45523 } else if (k === 'note') {
45525 Object.keys(source.note).forEach(function (id) {
45526 target.note[id] = osmNote(source.note[id]); // copy notes
45529 target[k] = JSON.parse(JSON.stringify(source[k])); // clone deep
45535 if (!arguments.length) {
45537 tile: cloneCache(_tileCache),
45538 note: cloneCache(_noteCache),
45539 user: cloneCache(_userCache)
45541 } // access caches directly for testing (e.g., loading notes rtree)
45544 if (obj === 'get') {
45553 _tileCache = obj.tile;
45554 _tileCache.inflight = {};
45558 _noteCache = obj.note;
45559 _noteCache.inflight = {};
45560 _noteCache.inflightPost = {};
45564 _userCache = obj.user;
45569 logout: function logout() {
45570 _userChangesets = undefined;
45571 _userDetails = undefined;
45573 dispatch$6.call('change');
45576 authenticated: function authenticated() {
45577 return oauth.authenticated();
45579 authenticate: function authenticate(callback) {
45581 var cid = _connectionID;
45582 _userChangesets = undefined;
45583 _userDetails = undefined;
45585 function done(err, res) {
45587 if (callback) callback(err);
45591 if (that.getConnectionId() !== cid) {
45592 if (callback) callback({
45593 message: 'Connection Switched',
45599 _rateLimitError = undefined;
45600 dispatch$6.call('change');
45601 if (callback) callback(err, res);
45602 that.userChangesets(function () {}); // eagerly load user details/changesets
45605 return oauth.authenticate(done);
45607 imageryBlocklists: function imageryBlocklists() {
45608 return _imageryBlocklists;
45610 tileZoom: function tileZoom(val) {
45611 if (!arguments.length) return _tileZoom$3;
45615 // get all cached notes covering the viewport
45616 notes: function notes(projection) {
45617 var viewport = projection.clipExtent();
45618 var min = [viewport[0][0], viewport[1][1]];
45619 var max = [viewport[1][0], viewport[0][1]];
45620 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
45621 return _noteCache.rtree.search(bbox).map(function (d) {
45625 // get a single note from the cache
45626 getNote: function getNote(id) {
45627 return _noteCache.note[id];
45629 // remove a single note from the cache
45630 removeNote: function removeNote(note) {
45631 if (!(note instanceof osmNote) || !note.id) return;
45632 delete _noteCache.note[note.id];
45633 updateRtree$3(encodeNoteRtree(note), false); // false = remove
45635 // replace a single note in the cache
45636 replaceNote: function replaceNote(note) {
45637 if (!(note instanceof osmNote) || !note.id) return;
45638 _noteCache.note[note.id] = note;
45639 updateRtree$3(encodeNoteRtree(note), true); // true = replace
45643 // Get an array of note IDs closed during this session.
45644 // Used to populate `closed:note` changeset tag
45645 getClosedIDs: function getClosedIDs() {
45646 return Object.keys(_noteCache.closed).sort();
45650 var _apibase = 'https://wiki.openstreetmap.org/w/api.php';
45651 var _inflight$1 = {};
45652 var _wikibaseCache = {};
45657 var debouncedRequest = debounce(request, 500, {
45661 function request(url, callback) {
45662 if (_inflight$1[url]) return;
45663 var controller = new AbortController();
45664 _inflight$1[url] = controller;
45666 signal: controller.signal
45667 }).then(function (result) {
45668 delete _inflight$1[url];
45669 if (callback) callback(null, result);
45670 })["catch"](function (err) {
45671 delete _inflight$1[url];
45672 if (err.name === 'AbortError') return;
45673 if (callback) callback(err.message);
45677 var serviceOsmWikibase = {
45678 init: function init() {
45680 _wikibaseCache = {};
45683 reset: function reset() {
45684 Object.values(_inflight$1).forEach(function (controller) {
45685 controller.abort();
45691 * Get the best value for the property, or undefined if not found
45692 * @param entity object from wikibase
45693 * @param property string e.g. 'P4' for image
45694 * @param langCode string e.g. 'fr' for French
45696 claimToValue: function claimToValue(entity, property, langCode) {
45697 if (!entity.claims[property]) return undefined;
45698 var locale = _localeIDs[langCode];
45699 var preferredPick, localePick;
45700 entity.claims[property].forEach(function (stmt) {
45701 // If exists, use value limited to the needed language (has a qualifier P26 = locale)
45702 // Or if not found, use the first value with the "preferred" rank
45703 if (!preferredPick && stmt.rank === 'preferred') {
45704 preferredPick = stmt;
45707 if (locale && stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === locale) {
45711 var result = localePick || preferredPick;
45714 var datavalue = result.mainsnak.datavalue;
45715 return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
45722 * Convert monolingual property into a key-value object (language -> value)
45723 * @param entity object from wikibase
45724 * @param property string e.g. 'P31' for monolingual wiki page title
45726 monolingualClaimToValueObj: function monolingualClaimToValueObj(entity, property) {
45727 if (!entity || !entity.claims[property]) return undefined;
45728 return entity.claims[property].reduce(function (acc, obj) {
45729 var value = obj.mainsnak.datavalue.value;
45730 acc[value.language] = value.text;
45734 toSitelink: function toSitelink(key, value) {
45735 var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
45736 return result.replace(/_/g, ' ').trim();
45739 // Pass params object of the form:
45742 // value: 'string',
45743 // langCode: 'string'
45746 getEntity: function getEntity(params, callback) {
45747 var doRequest = params.debounce ? debouncedRequest : request;
45751 var rtypeSitelink = params.key === 'type' && params.value ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
45752 var keySitelink = params.key ? this.toSitelink(params.key) : false;
45753 var tagSitelink = params.key && params.value ? this.toSitelink(params.key, params.value) : false;
45754 var localeSitelink;
45756 if (params.langCodes) {
45757 params.langCodes.forEach(function (langCode) {
45758 if (_localeIDs[langCode] === undefined) {
45759 // If this is the first time we are asking about this locale,
45760 // fetch corresponding entity (if it exists), and cache it.
45761 // If there is no such entry, cache `false` value to avoid re-requesting it.
45762 localeSitelink = ('Locale:' + langCode).replace(/_/g, ' ').trim();
45763 titles.push(localeSitelink);
45768 if (rtypeSitelink) {
45769 if (_wikibaseCache[rtypeSitelink]) {
45770 result.rtype = _wikibaseCache[rtypeSitelink];
45772 titles.push(rtypeSitelink);
45777 if (_wikibaseCache[keySitelink]) {
45778 result.key = _wikibaseCache[keySitelink];
45780 titles.push(keySitelink);
45785 if (_wikibaseCache[tagSitelink]) {
45786 result.tag = _wikibaseCache[tagSitelink];
45788 titles.push(tagSitelink);
45792 if (!titles.length) {
45793 // Nothing to do, we already had everything in the cache
45794 return callback(null, result);
45795 } // Requesting just the user language code
45796 // If backend recognizes the code, it will perform proper fallbacks,
45797 // and the result will contain the requested code. If not, all values are returned:
45798 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
45799 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
45803 action: 'wbgetentities',
45805 titles: titles.join('|'),
45806 languages: params.langCodes.join('|'),
45807 languagefallback: 1,
45809 format: 'json' // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
45810 // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
45811 // formatversion: 2,
45814 var url = _apibase + '?' + utilQsString(obj);
45815 doRequest(url, function (err, d) {
45818 } else if (!d.success || d.error) {
45819 callback(d.error.messages.map(function (v) {
45820 return v.html['*'];
45823 var localeID = false;
45824 Object.values(d.entities).forEach(function (res) {
45825 if (res.missing !== '') {
45826 var title = res.sitelinks.wiki.title;
45828 if (title === rtypeSitelink) {
45829 _wikibaseCache[rtypeSitelink] = res;
45830 result.rtype = res;
45831 } else if (title === keySitelink) {
45832 _wikibaseCache[keySitelink] = res;
45834 } else if (title === tagSitelink) {
45835 _wikibaseCache[tagSitelink] = res;
45837 } else if (title === localeSitelink) {
45840 console.log('Unexpected title ' + title); // eslint-disable-line no-console
45845 if (localeSitelink) {
45846 // If locale ID is not found, store false to prevent repeated queries
45847 that.addLocale(params.langCodes[0], localeID);
45850 callback(null, result);
45855 // Pass params object of the form:
45857 // key: 'string', // required
45858 // value: 'string' // optional
45861 // Get an result object used to display tag documentation
45863 // title: 'string',
45864 // description: 'string',
45865 // editURL: 'string',
45866 // imageURL: 'string',
45867 // wiki: { title: 'string', text: 'string', url: 'string' }
45870 getDocs: function getDocs(params, callback) {
45872 var langCodes = _mainLocalizer.localeCodes().map(function (code) {
45873 return code.toLowerCase();
45875 params.langCodes = langCodes;
45876 this.getEntity(params, function (err, data) {
45882 var entity = data.rtype || data.tag || data.key;
45885 callback('No entity');
45892 for (i in langCodes) {
45893 var _code = langCodes[i];
45895 if (entity.descriptions[_code] && entity.descriptions[_code].language === _code) {
45896 description = entity.descriptions[_code];
45901 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
45904 title: entity.title,
45905 description: description ? description.value : '',
45906 descriptionLocaleCode: description ? description.language : '',
45907 editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
45910 if (entity.claims) {
45912 var image = that.claimToValue(entity, 'P4', langCodes[0]);
45915 imageroot = 'https://commons.wikimedia.org/w/index.php';
45917 image = that.claimToValue(entity, 'P28', langCodes[0]);
45920 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
45924 if (imageroot && image) {
45925 result.imageURL = imageroot + '?' + utilQsString({
45926 title: 'Special:Redirect/file/' + image,
45930 } // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
45931 // If neither tag nor key data item contain a wiki page in the needed language nor English,
45932 // get the first found wiki page from either the tag or the key item.
45935 var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
45936 var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
45937 var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
45938 var wikis = [rtypeWiki, tagWiki, keyWiki];
45941 var wiki = wikis[i];
45943 for (var j in langCodes) {
45944 var code = langCodes[j];
45945 var referenceId = langCodes[0].split('-')[0] !== 'en' && code.split('-')[0] === 'en' ? 'inspector.wiki_en_reference' : 'inspector.wiki_reference';
45946 var info = getWikiInfo(wiki, code, referenceId);
45949 result.wiki = info;
45954 if (result.wiki) break;
45957 callback(null, result); // Helper method to get wiki info if a given language exists
45959 function getWikiInfo(wiki, langCode, tKey) {
45960 if (wiki && wiki[langCode]) {
45962 title: wiki[langCode],
45964 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
45970 addLocale: function addLocale(langCode, qid) {
45971 // Makes it easier to unit test
45972 _localeIDs[langCode] = qid;
45974 apibase: function apibase(val) {
45975 if (!arguments.length) return _apibase;
45981 var jsonpCache = {};
45982 window.jsonpCache = jsonpCache;
45983 function jsonpRequest(url, callback) {
45985 abort: function abort() {}
45988 if (window.JSONP_FIX) {
45989 if (window.JSONP_DELAY === 0) {
45990 callback(window.JSONP_FIX);
45992 var t = window.setTimeout(function () {
45993 callback(window.JSONP_FIX);
45994 }, window.JSONP_DELAY || 0);
45996 request.abort = function () {
45997 window.clearTimeout(t);
46005 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
46010 c += chars.charAt(Math.floor(Math.random() * 52));
46016 function create(url) {
46017 var e = url.match(/callback=(\w+)/);
46018 var c = e ? e[1] : rand();
46020 jsonpCache[c] = function (data) {
46021 if (jsonpCache[c]) {
46028 function finalize() {
46029 delete jsonpCache[c];
46033 request.abort = finalize;
46034 return 'jsonpCache.' + c;
46037 var cb = create(url);
46038 var script = select('head').append('script').attr('type', 'text/javascript').attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
46042 var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
46043 var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
46044 var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
46045 var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
46046 var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
46047 var maxResults$2 = 2000;
46048 var tileZoom$2 = 16.5;
46049 var tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
46050 var dispatch$7 = dispatch('loadedImages', 'viewerChanged');
46051 var minHfov = 10; // zoom in degrees: 20, 10, 5
46053 var maxHfov = 90; // zoom out degrees
46055 var defaultHfov = 45;
46056 var _hires = false;
46057 var _resolution = 512; // higher numbers are slower - 512, 1024, 2048, 4096
46059 var _currScene = 0;
46063 var _pannellumViewer;
46065 var _sceneOptions = {
46066 showFullscreenCtrl: false,
46077 var _loadViewerPromise$2;
46083 function abortRequest$6(i) {
46087 * localeTimeStamp().
46091 function localeTimestamp(s) {
46092 if (!s) return null;
46098 var d = new Date(s);
46099 if (isNaN(d.getTime())) return null;
46100 return d.toLocaleString(_mainLocalizer.localeCode(), options);
46103 * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
46107 function loadTiles$2(which, url, projection, margin) {
46108 var tiles = tiler$6.margin(margin).getTiles(projection); // abort inflight requests that are no longer needed
46110 var cache = _ssCache[which];
46111 Object.keys(cache.inflight).forEach(function (k) {
46112 var wanted = tiles.find(function (tile) {
46113 return k.indexOf(tile.id + ',') === 0;
46117 abortRequest$6(cache.inflight[k]);
46118 delete cache.inflight[k];
46121 tiles.forEach(function (tile) {
46122 return loadNextTilePage$2(which, url, tile);
46126 * loadNextTilePage() load data for the next tile page in line.
46130 function loadNextTilePage$2(which, url, tile) {
46131 var cache = _ssCache[which];
46132 var nextPage = cache.nextPage[tile.id] || 0;
46133 var id = tile.id + ',' + String(nextPage);
46134 if (cache.loaded[id] || cache.inflight[id]) return;
46135 cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
46136 cache.loaded[id] = true;
46137 delete cache.inflight[id];
46138 if (!bubbles) return; // [].shift() removes the first element, some statistics info, not a bubble point
46141 var features = bubbles.map(function (bubble) {
46142 if (cache.points[bubble.id]) return null; // skip duplicates
46144 var loc = [bubble.lo, bubble.la];
46149 captured_at: bubble.cd,
46150 captured_by: 'microsoft',
46151 // nbn: bubble.nbn,
46152 // pbn: bubble.pbn,
46162 cache.points[bubble.id] = d; // a sequence starts here
46164 if (bubble.pr === undefined) {
46165 cache.leaders.push(bubble.id);
46175 }).filter(Boolean);
46176 cache.rtree.load(features);
46177 connectSequences();
46179 if (which === 'bubbles') {
46180 dispatch$7.call('loadedImages');
46183 } // call this sometimes to connect the bubbles into sequences
46186 function connectSequences() {
46187 var cache = _ssCache.bubbles;
46188 var keepLeaders = [];
46190 for (var i = 0; i < cache.leaders.length; i++) {
46191 var bubble = cache.points[cache.leaders[i]];
46192 var seen = {}; // try to make a sequence.. use the key of the leader bubble.
46198 var complete = false;
46201 sequence.bubbles.push(bubble);
46202 seen[bubble.key] = true;
46204 if (bubble.ne === undefined) {
46207 bubble = cache.points[bubble.ne]; // advance to next
46209 } while (bubble && !seen[bubble.key] && !complete);
46212 _ssCache.sequences[sequence.key] = sequence; // assign bubbles to the sequence
46214 for (var j = 0; j < sequence.bubbles.length; j++) {
46215 sequence.bubbles[j].sequenceKey = sequence.key;
46216 } // create a GeoJSON LineString
46219 sequence.geojson = {
46220 type: 'LineString',
46222 captured_at: sequence.bubbles[0] ? sequence.bubbles[0].captured_at : null,
46223 captured_by: sequence.bubbles[0] ? sequence.bubbles[0].captured_by : null,
46226 coordinates: sequence.bubbles.map(function (d) {
46231 keepLeaders.push(cache.leaders[i]);
46233 } // couldn't complete these, save for later
46236 cache.leaders = keepLeaders;
46239 * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
46243 function getBubbles(url, tile, callback) {
46244 var rect = tile.extent.rectangle();
46245 var urlForRequest = url + utilQsString({
46251 appkey: bubbleAppKey,
46252 jsCallback: '{callback}'
46254 return jsonpRequest(urlForRequest, function (data) {
46255 if (!data || data.error) {
46261 } // partition viewport into higher zoom tiles
46264 function partitionViewport$2(projection) {
46265 var z = geoScaleToZoom(projection.scale());
46266 var z2 = Math.ceil(z * 2) / 2 + 2.5; // round to next 0.5 and add 2.5
46268 var tiler = utilTiler().zoomExtent([z2, z2]);
46269 return tiler.getTiles(projection).map(function (tile) {
46270 return tile.extent;
46272 } // no more than `limit` results per partition.
46275 function searchLimited$2(limit, projection, rtree) {
46276 limit = limit || 5;
46277 return partitionViewport$2(projection).reduce(function (result, extent) {
46278 var found = rtree.search(extent.bbox()).slice(0, limit).map(function (d) {
46281 return found.length ? result.concat(found) : result;
46289 function loadImage(imgInfo) {
46290 return new Promise(function (resolve) {
46291 var img = new Image();
46293 img.onload = function () {
46294 var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
46295 var ctx = canvas.getContext('2d');
46296 ctx.drawImage(img, imgInfo.x, imgInfo.y);
46303 img.onerror = function () {
46310 img.setAttribute('crossorigin', '');
46311 img.src = imgInfo.url;
46319 function loadCanvas(imageGroup) {
46320 return Promise.all(imageGroup.map(loadImage)).then(function (data) {
46321 var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
46330 var face = data[0].imgInfo.face;
46331 _sceneOptions.cubeMap[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
46333 status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'
46342 function loadFaces(faceGroup) {
46343 return Promise.all(faceGroup.map(loadCanvas)).then(function () {
46345 status: 'loadFaces done'
46350 function setupCanvas(selection, reset) {
46352 selection.selectAll('#ideditor-stitcher-canvases').remove();
46353 } // Add the Streetside working canvases. These are used for 'stitching', or combining,
46354 // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
46357 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) {
46358 return 'ideditor-' + d;
46359 }).attr('width', _resolution).attr('height', _resolution);
46362 function qkToXY(qk) {
46367 for (var i = qk.length; i > 0; i--) {
46368 var key = qk[i - 1];
46369 x += +(key === '1' || key === '3') * scale;
46370 y += +(key === '2' || key === '3') * scale;
46377 function getQuadKeys() {
46378 var dim = _resolution / 256;
46382 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'];
46383 } else if (dim === 8) {
46384 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'];
46385 } else if (dim === 4) {
46386 quadKeys = ['00', '01', '10', '11', '02', '03', '12', '13', '20', '21', '30', '31', '22', '23', '32', '33'];
46389 quadKeys = ['0', '1', '2', '3'];
46395 var serviceStreetside = {
46397 * init() initialize streetside.
46399 init: function init() {
46404 this.event = utilRebind(this, dispatch$7, 'on');
46408 * reset() reset the cache.
46410 reset: function reset() {
46412 Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
46420 rtree: new RBush(),
46431 bubbles: function bubbles(projection) {
46433 return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
46435 cachedImage: function cachedImage(imageKey) {
46436 return _ssCache.bubbles.points[imageKey];
46438 sequences: function sequences(projection) {
46439 var viewport = projection.clipExtent();
46440 var min = [viewport[0][0], viewport[1][1]];
46441 var max = [viewport[1][0], viewport[0][1]];
46442 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
46444 var results = []; // all sequences for bubbles in viewport
46446 _ssCache.bubbles.rtree.search(bbox).forEach(function (d) {
46447 var key = d.data.sequenceKey;
46449 if (key && !seen[key]) {
46451 results.push(_ssCache.sequences[key].geojson);
46461 loadBubbles: function loadBubbles(projection, margin) {
46462 // by default: request 2 nearby tiles so we can connect sequences.
46463 if (margin === undefined) margin = 2;
46464 loadTiles$2('bubbles', bubbleApi, projection, margin);
46466 viewer: function viewer() {
46467 return _pannellumViewer;
46469 initViewer: function initViewer() {
46470 if (!window.pannellum) return;
46471 if (_pannellumViewer) return;
46474 var sceneID = _currScene.toString();
46478 firstScene: sceneID
46482 options.scenes[sceneID] = _sceneOptions;
46483 _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
46485 ensureViewerLoaded: function ensureViewerLoaded(context) {
46486 if (_loadViewerPromise$2) return _loadViewerPromise$2; // create ms-wrapper, a photo wrapper class
46488 var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper').data([0]); // inject ms-wrapper into the photoviewer div
46489 // (used by all to house each custom photo viewer)
46491 var wrapEnter = wrap.enter().append('div').attr('class', 'photo-wrapper ms-wrapper').classed('hide', true);
46493 var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // inject div to support streetside viewer (pannellum) and attribution line
46495 wrapEnter.append('div').attr('id', 'ideditor-viewer-streetside').on(pointerPrefix + 'down.streetside', function () {
46496 select(window).on(pointerPrefix + 'move.streetside', function () {
46497 dispatch$7.call('viewerChanged');
46499 }).on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
46500 select(window).on(pointerPrefix + 'move.streetside', null); // continue dispatching events for a few seconds, in case viewer has inertia.
46502 var t = timer(function (elapsed) {
46503 dispatch$7.call('viewerChanged');
46505 if (elapsed > 2000) {
46509 }).append('div').attr('class', 'photo-attribution fillD');
46510 var controlsEnter = wrapEnter.append('div').attr('class', 'photo-controls-wrap').append('div').attr('class', 'photo-controls');
46511 controlsEnter.append('button').on('click.back', step(-1)).html('◄');
46512 controlsEnter.append('button').on('click.forward', step(1)).html('►'); // create working canvas for stitching together images
46514 wrap = wrap.merge(wrapEnter).call(setupCanvas, true); // Register viewer resize handler
46516 context.ui().photoviewer.on('resize.streetside', function () {
46517 if (_pannellumViewer) {
46518 _pannellumViewer.resize();
46521 _loadViewerPromise$2 = new Promise(function (resolve, reject) {
46522 var loadedCount = 0;
46524 function loaded() {
46525 loadedCount += 1; // wait until both files are loaded
46527 if (loadedCount === 2) resolve();
46530 var head = select('head'); // load streetside pannellum viewer css
46532 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 () {
46534 }); // load streetside pannellum viewer js
46536 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 () {
46539 })["catch"](function () {
46540 _loadViewerPromise$2 = null;
46542 return _loadViewerPromise$2;
46544 function step(stepBy) {
46545 return function () {
46546 var viewer = context.container().select('.photoviewer');
46547 var selected = viewer.empty() ? undefined : viewer.datum();
46548 if (!selected) return;
46549 var nextID = stepBy === 1 ? selected.ne : selected.pr;
46551 var yaw = _pannellumViewer.getYaw();
46553 var ca = selected.ca + yaw;
46554 var origin = selected.loc; // construct a search trapezoid pointing out from current bubble
46557 var p1 = [origin[0] + geoMetersToLon(meters / 5, origin[1]), origin[1]];
46558 var p2 = [origin[0] + geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
46559 var p3 = [origin[0] - geoMetersToLon(meters / 2, origin[1]), origin[1] + geoMetersToLat(meters)];
46560 var p4 = [origin[0] - geoMetersToLon(meters / 5, origin[1]), origin[1]];
46561 var poly = [p1, p2, p3, p4, p1]; // rotate it to face forward/backward
46563 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
46564 poly = geoRotate(poly, -angle, origin);
46565 var extent = poly.reduce(function (extent, point) {
46566 return extent.extend(geoExtent(point));
46567 }, geoExtent()); // find nearest other bubble in the search polygon
46569 var minDist = Infinity;
46571 _ssCache.bubbles.rtree.search(extent.bbox()).forEach(function (d) {
46572 if (d.data.key === selected.key) return;
46573 if (!geoPointInPolygon(d.data.loc, poly)) return;
46574 var dist = geoVecLength(d.data.loc, selected.loc);
46575 var theta = selected.ca - d.data.ca;
46576 var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
46578 if (minTheta > 20) {
46579 dist += 5; // penalize distance if camera angles don't match
46582 if (dist < minDist) {
46583 nextID = d.data.key;
46588 var nextBubble = nextID && that.cachedImage(nextID);
46589 if (!nextBubble) return;
46590 context.map().centerEase(nextBubble.loc);
46591 that.selectImage(context, nextBubble.key).yaw(yaw).showViewer(context);
46595 yaw: function yaw(_yaw) {
46596 if (typeof _yaw !== 'number') return _yaw;
46597 _sceneOptions.yaw = _yaw;
46604 showViewer: function showViewer(context) {
46605 var wrap = context.container().select('.photoviewer').classed('hide', false);
46606 var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
46609 wrap.selectAll('.photo-wrapper:not(.ms-wrapper)').classed('hide', true);
46610 wrap.selectAll('.photo-wrapper.ms-wrapper').classed('hide', false);
46619 hideViewer: function hideViewer(context) {
46620 var viewer = context.container().select('.photoviewer');
46621 if (!viewer.empty()) viewer.datum(null);
46622 viewer.classed('hide', true).selectAll('.photo-wrapper').classed('hide', true);
46623 context.container().selectAll('.viewfield-group, .sequence, .icon-sign').classed('currentView', false);
46624 this.updateUrlImage(null);
46625 return this.setStyles(context, null, true);
46631 selectImage: function selectImage(context, key) {
46633 var d = this.cachedImage(key);
46634 var viewer = context.container().select('.photoviewer');
46635 if (!viewer.empty()) viewer.datum(d);
46636 this.setStyles(context, null, true);
46637 var wrap = context.container().select('.photoviewer .ms-wrapper');
46638 var attribution = wrap.selectAll('.photo-attribution').html('');
46639 wrap.selectAll('.pnlm-load-box') // display "loading.."
46640 .style('display', 'block');
46641 if (!d) return this;
46642 this.updateUrlImage(key);
46643 _sceneOptions.northOffset = d.ca;
46644 var line1 = attribution.append('div').attr('class', 'attribution-row');
46645 var hiresDomId = utilUniqueDomId('streetside-hires'); // Add hires checkbox
46647 var label = line1.append('label').attr('for', hiresDomId).attr('class', 'streetside-hires');
46648 label.append('input').attr('type', 'checkbox').attr('id', hiresDomId).property('checked', _hires).on('click', function (d3_event) {
46649 d3_event.stopPropagation();
46651 _resolution = _hires ? 1024 : 512;
46652 wrap.call(setupCanvas, true);
46654 yaw: _pannellumViewer.getYaw(),
46655 pitch: _pannellumViewer.getPitch(),
46656 hfov: _pannellumViewer.getHfov()
46658 _sceneOptions = Object.assign(_sceneOptions, viewstate);
46659 that.selectImage(context, d.key).showViewer(context);
46661 label.append('span').html(_t.html('streetside.hires'));
46662 var captureInfo = line1.append('div').attr('class', 'attribution-capture-info'); // Add capture date
46664 if (d.captured_by) {
46665 var yyyy = new Date().getFullYear();
46666 captureInfo.append('a').attr('class', 'captured_by').attr('target', '_blank').attr('href', 'https://www.microsoft.com/en-us/maps/streetside').html('©' + yyyy + ' Microsoft');
46667 captureInfo.append('span').html('|');
46670 if (d.captured_at) {
46671 captureInfo.append('span').attr('class', 'captured_at').html(localeTimestamp(d.captured_at));
46672 } // Add image links
46675 var line2 = attribution.append('div').attr('class', 'attribution-row');
46676 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'));
46677 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'));
46678 var bubbleIdQuadKey = d.key.toString(4);
46679 var paddingNeeded = 16 - bubbleIdQuadKey.length;
46681 for (var i = 0; i < paddingNeeded; i++) {
46682 bubbleIdQuadKey = '0' + bubbleIdQuadKey;
46685 var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
46686 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
46688 var faceKeys = ['01', '02', '03', '10', '11', '12']; // Map images to cube faces
46690 var quadKeys = getQuadKeys();
46691 var faces = faceKeys.map(function (faceKey) {
46692 return quadKeys.map(function (quadKey) {
46693 var xy = qkToXY(quadKey);
46696 url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
46702 loadFaces(faces).then(function () {
46703 if (!_pannellumViewer) {
46706 // make a new scene
46709 var sceneID = _currScene.toString();
46711 _pannellumViewer.addScene(sceneID, _sceneOptions).loadScene(sceneID); // remove previous scene
46714 if (_currScene > 2) {
46715 sceneID = (_currScene - 1).toString();
46717 _pannellumViewer.removeScene(sceneID);
46723 getSequenceKeyForBubble: function getSequenceKeyForBubble(d) {
46724 return d && d.sequenceKey;
46726 // Updates the currently highlighted sequence and selected bubble.
46727 // Reset is only necessary when interacting with the viewport because
46728 // this implicitly changes the currently selected bubble/sequence
46729 setStyles: function setStyles(context, hovered, reset) {
46731 // reset all layers
46732 context.container().selectAll('.viewfield-group').classed('highlighted', false).classed('hovered', false).classed('currentView', false);
46733 context.container().selectAll('.sequence').classed('highlighted', false).classed('currentView', false);
46736 var hoveredBubbleKey = hovered && hovered.key;
46737 var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
46738 var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
46739 var hoveredBubbleKeys = hoveredSequence && hoveredSequence.bubbles.map(function (d) {
46742 var viewer = context.container().select('.photoviewer');
46743 var selected = viewer.empty() ? undefined : viewer.datum();
46744 var selectedBubbleKey = selected && selected.key;
46745 var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
46746 var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
46747 var selectedBubbleKeys = selectedSequence && selectedSequence.bubbles.map(function (d) {
46749 }) || []; // highlight sibling viewfields on either the selected or the hovered sequences
46751 var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
46752 context.container().selectAll('.layer-streetside-images .viewfield-group').classed('highlighted', function (d) {
46753 return highlightedBubbleKeys.indexOf(d.key) !== -1;
46754 }).classed('hovered', function (d) {
46755 return d.key === hoveredBubbleKey;
46756 }).classed('currentView', function (d) {
46757 return d.key === selectedBubbleKey;
46759 context.container().selectAll('.layer-streetside-images .sequence').classed('highlighted', function (d) {
46760 return d.properties.key === hoveredSequenceKey;
46761 }).classed('currentView', function (d) {
46762 return d.properties.key === selectedSequenceKey;
46763 }); // update viewfields if needed
46765 context.container().selectAll('.viewfield-group .viewfield').attr('d', viewfieldPath);
46767 function viewfieldPath() {
46768 var d = this.parentNode.__data__;
46770 if (d.pano && d.key !== selectedBubbleKey) {
46771 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
46773 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
46779 updateUrlImage: function updateUrlImage(imageKey) {
46780 if (!window.mocha) {
46781 var hash = utilStringQs(window.location.hash);
46784 hash.photo = 'streetside/' + imageKey;
46789 window.location.replace('#' + utilQsString(hash, true));
46796 cache: function cache() {
46801 var _apibase$1 = 'https://taginfo.openstreetmap.org/api/4/';
46802 var _inflight$2 = {};
46803 var _popularKeys = {};
46804 var _taginfoCache = {};
46806 point: 'count_nodes',
46807 vertex: 'count_nodes',
46808 area: 'count_ways',
46811 var tag_sort_members = {
46812 point: 'count_node_members',
46813 vertex: 'count_node_members',
46814 area: 'count_way_members',
46815 line: 'count_way_members',
46816 relation: 'count_relation_members'
46818 var tag_filters = {
46824 var tag_members_fractions = {
46825 point: 'count_node_members_fraction',
46826 vertex: 'count_node_members_fraction',
46827 area: 'count_way_members_fraction',
46828 line: 'count_way_members_fraction',
46829 relation: 'count_relation_members_fraction'
46832 function sets(params, n, o) {
46833 if (params.geometry && o[params.geometry]) {
46834 params[n] = o[params.geometry];
46840 function setFilter(params) {
46841 return sets(params, 'filter', tag_filters);
46844 function setSort(params) {
46845 return sets(params, 'sortname', tag_sorts);
46848 function setSortMembers(params) {
46849 return sets(params, 'sortname', tag_sort_members);
46852 function clean(params) {
46853 return utilObjectOmit(params, ['geometry', 'debounce']);
46856 function filterKeys(type) {
46857 var count_type = type ? 'count_' + type : 'count_all';
46858 return function (d) {
46859 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
46863 function filterMultikeys(prefix) {
46864 return function (d) {
46865 // d.key begins with prefix, and d.key contains no additional ':'s
46866 var re = new RegExp('^' + prefix + '(.*)$');
46867 var matches = d.key.match(re) || [];
46868 return matches.length === 2 && matches[1].indexOf(':') === -1;
46872 function filterValues(allowUpperCase) {
46873 return function (d) {
46874 if (d.value.match(/[;,]/) !== null) return false; // exclude some punctuation
46876 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) return false; // exclude uppercase letters
46878 return parseFloat(d.fraction) > 0.0;
46882 function filterRoles(geometry) {
46883 return function (d) {
46884 if (d.role === '') return false; // exclude empty role
46886 if (d.role.match(/[A-Z*;,]/) !== null) return false; // exclude uppercase letters and some punctuation
46888 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
46892 function valKey(d) {
46899 function valKeyDescription(d) {
46902 title: d.description || d.value
46906 obj.count = d.count;
46912 function roleKey(d) {
46917 } // sort keys with ':' lower than keys without ':'
46920 function sortKeys(a, b) {
46921 return a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1 ? -1 : a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1 ? 1 : 0;
46924 var debouncedRequest$1 = debounce(request$1, 300, {
46928 function request$1(url, params, exactMatch, callback, loaded) {
46929 if (_inflight$2[url]) return;
46930 if (checkCache(url, params, exactMatch, callback)) return;
46931 var controller = new AbortController();
46932 _inflight$2[url] = controller;
46934 signal: controller.signal
46935 }).then(function (result) {
46936 delete _inflight$2[url];
46937 if (loaded) loaded(null, result);
46938 })["catch"](function (err) {
46939 delete _inflight$2[url];
46940 if (err.name === 'AbortError') return;
46941 if (loaded) loaded(err.message);
46945 function checkCache(url, params, exactMatch, callback) {
46946 var rp = params.rp || 25;
46947 var testQuery = params.query || '';
46951 var hit = _taginfoCache[testUrl]; // exact match, or shorter match yielding fewer than max results (rp)
46953 if (hit && (url === testUrl || hit.length < rp)) {
46954 callback(null, hit);
46956 } // don't try to shorten the query
46959 if (exactMatch || !testQuery.length) return false; // do shorten the query to see if we already have a cached result
46960 // that has returned fewer than max results (rp)
46962 testQuery = testQuery.slice(0, -1);
46963 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
46964 } while (testQuery.length >= 0);
46969 var serviceTaginfo = {
46970 init: function init() {
46972 _taginfoCache = {};
46974 // manually exclude some keys – #5377, #7485
46980 sorting_name: true,
46984 'bridge:name': true
46985 }; // Fetch popular keys. We'll exclude these from `values`
46986 // lookups because they stress taginfo, and they aren't likely
46987 // to yield meaningful autocomplete results.. see #3955
46991 sortname: 'values_all',
46995 lang: _mainLocalizer.languageCode()
46997 this.keys(params, function (err, data) {
46999 data.forEach(function (d) {
47000 if (d.value === 'opening_hours') return; // exception
47002 _popularKeys[d.value] = true;
47006 reset: function reset() {
47007 Object.values(_inflight$2).forEach(function (controller) {
47008 controller.abort();
47012 keys: function keys(params, callback) {
47013 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47014 params = clean(setSort(params));
47015 params = Object.assign({
47017 sortname: 'count_all',
47020 lang: _mainLocalizer.languageCode()
47022 var url = _apibase$1 + 'keys/all?' + utilQsString(params);
47023 doRequest(url, params, false, callback, function (err, d) {
47027 var f = filterKeys(params.filter);
47028 var result = d.data.filter(f).sort(sortKeys).map(valKey);
47029 _taginfoCache[url] = result;
47030 callback(null, result);
47034 multikeys: function multikeys(params, callback) {
47035 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47036 params = clean(setSort(params));
47037 params = Object.assign({
47039 sortname: 'count_all',
47042 lang: _mainLocalizer.languageCode()
47044 var prefix = params.query;
47045 var url = _apibase$1 + 'keys/all?' + utilQsString(params);
47046 doRequest(url, params, true, callback, function (err, d) {
47050 var f = filterMultikeys(prefix);
47051 var result = d.data.filter(f).map(valKey);
47052 _taginfoCache[url] = result;
47053 callback(null, result);
47057 values: function values(params, callback) {
47058 // Exclude popular keys from values lookups.. see #3955
47059 var key = params.key;
47061 if (key && _popularKeys[key]) {
47062 callback(null, []);
47066 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47067 params = clean(setSort(setFilter(params)));
47068 params = Object.assign({
47070 sortname: 'count_all',
47073 lang: _mainLocalizer.languageCode()
47075 var url = _apibase$1 + 'key/values?' + utilQsString(params);
47076 doRequest(url, params, false, callback, function (err, d) {
47080 // In most cases we prefer taginfo value results with lowercase letters.
47081 // A few OSM keys expect values to contain uppercase values (see #3377).
47082 // This is not an exhaustive list (e.g. `name` also has uppercase values)
47083 // but these are the fields where taginfo value lookup is most useful.
47084 var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
47085 var allowUpperCase = re.test(params.key);
47086 var f = filterValues(allowUpperCase);
47087 var result = d.data.filter(f).map(valKeyDescription);
47088 _taginfoCache[url] = result;
47089 callback(null, result);
47093 roles: function roles(params, callback) {
47094 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47095 var geometry = params.geometry;
47096 params = clean(setSortMembers(params));
47097 params = Object.assign({
47099 sortname: 'count_all_members',
47102 lang: _mainLocalizer.languageCode()
47104 var url = _apibase$1 + 'relation/roles?' + utilQsString(params);
47105 doRequest(url, params, true, callback, function (err, d) {
47109 var f = filterRoles(geometry);
47110 var result = d.data.filter(f).map(roleKey);
47111 _taginfoCache[url] = result;
47112 callback(null, result);
47116 docs: function docs(params, callback) {
47117 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
47118 params = clean(setSort(params));
47119 var path = 'key/wiki_pages?';
47121 if (params.value) {
47122 path = 'tag/wiki_pages?';
47123 } else if (params.rtype) {
47124 path = 'relation/wiki_pages?';
47127 var url = _apibase$1 + path + utilQsString(params);
47128 doRequest(url, params, true, callback, function (err, d) {
47132 _taginfoCache[url] = d.data;
47133 callback(null, d.data);
47137 apibase: function apibase(_) {
47138 if (!arguments.length) return _apibase$1;
47144 var helpers$1 = createCommonjsModule(function (module, exports) {
47146 Object.defineProperty(exports, "__esModule", {
47154 * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
47156 * @memberof helpers
47160 exports.earthRadius = 6371008.8;
47162 * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
47164 * @memberof helpers
47168 exports.factors = {
47169 centimeters: exports.earthRadius * 100,
47170 centimetres: exports.earthRadius * 100,
47171 degrees: exports.earthRadius / 111325,
47172 feet: exports.earthRadius * 3.28084,
47173 inches: exports.earthRadius * 39.370,
47174 kilometers: exports.earthRadius / 1000,
47175 kilometres: exports.earthRadius / 1000,
47176 meters: exports.earthRadius,
47177 metres: exports.earthRadius,
47178 miles: exports.earthRadius / 1609.344,
47179 millimeters: exports.earthRadius * 1000,
47180 millimetres: exports.earthRadius * 1000,
47181 nauticalmiles: exports.earthRadius / 1852,
47183 yards: exports.earthRadius / 1.0936
47186 * Units of measurement factors based on 1 meter.
47188 * @memberof helpers
47192 exports.unitsFactors = {
47195 degrees: 1 / 111325,
47198 kilometers: 1 / 1000,
47199 kilometres: 1 / 1000,
47202 miles: 1 / 1609.344,
47205 nauticalmiles: 1 / 1852,
47206 radians: 1 / exports.earthRadius,
47210 * Area of measurement factors based on 1 square meter.
47212 * @memberof helpers
47216 exports.areaFactors = {
47217 acres: 0.000247105,
47218 centimeters: 10000,
47219 centimetres: 10000,
47220 feet: 10.763910417,
47221 inches: 1550.003100006,
47222 kilometers: 0.000001,
47223 kilometres: 0.000001,
47227 millimeters: 1000000,
47228 millimetres: 1000000,
47232 * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
47235 * @param {Geometry} geometry input geometry
47236 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47237 * @param {Object} [options={}] Optional Parameters
47238 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47239 * @param {string|number} [options.id] Identifier associated with the Feature
47240 * @returns {Feature} a GeoJSON Feature
47244 * "coordinates": [110, 50]
47247 * var feature = turf.feature(geometry);
47252 function feature(geom, properties, options) {
47253 if (options === void 0) {
47261 if (options.id === 0 || options.id) {
47262 feat.id = options.id;
47265 if (options.bbox) {
47266 feat.bbox = options.bbox;
47269 feat.properties = properties || {};
47270 feat.geometry = geom;
47274 exports.feature = feature;
47276 * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
47277 * For GeometryCollection type use `helpers.geometryCollection`
47280 * @param {string} type Geometry Type
47281 * @param {Array<any>} coordinates Coordinates
47282 * @param {Object} [options={}] Optional Parameters
47283 * @returns {Geometry} a GeoJSON Geometry
47285 * var type = "Point";
47286 * var coordinates = [110, 50];
47287 * var geometry = turf.geometry(type, coordinates);
47291 function geometry(type, coordinates, options) {
47295 return point(coordinates).geometry;
47298 return lineString(coordinates).geometry;
47301 return polygon(coordinates).geometry;
47304 return multiPoint(coordinates).geometry;
47306 case "MultiLineString":
47307 return multiLineString(coordinates).geometry;
47309 case "MultiPolygon":
47310 return multiPolygon(coordinates).geometry;
47313 throw new Error(type + " is invalid");
47317 exports.geometry = geometry;
47319 * Creates a {@link Point} {@link Feature} from a Position.
47322 * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
47323 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47324 * @param {Object} [options={}] Optional Parameters
47325 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47326 * @param {string|number} [options.id] Identifier associated with the Feature
47327 * @returns {Feature<Point>} a Point feature
47329 * var point = turf.point([-75.343, 39.984]);
47334 function point(coordinates, properties, options) {
47335 if (options === void 0) {
47341 coordinates: coordinates
47343 return feature(geom, properties, options);
47346 exports.point = point;
47348 * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
47351 * @param {Array<Array<number>>} coordinates an array of Points
47352 * @param {Object} [properties={}] Translate these properties to each Feature
47353 * @param {Object} [options={}] Optional Parameters
47354 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
47355 * associated with the FeatureCollection
47356 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47357 * @returns {FeatureCollection<Point>} Point Feature
47359 * var points = turf.points([
47368 function points(coordinates, properties, options) {
47369 if (options === void 0) {
47373 return featureCollection(coordinates.map(function (coords) {
47374 return point(coords, properties);
47378 exports.points = points;
47380 * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
47383 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
47384 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47385 * @param {Object} [options={}] Optional Parameters
47386 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47387 * @param {string|number} [options.id] Identifier associated with the Feature
47388 * @returns {Feature<Polygon>} Polygon Feature
47390 * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
47395 function polygon(coordinates, properties, options) {
47396 if (options === void 0) {
47400 for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
47401 var ring = coordinates_1[_i];
47403 if (ring.length < 4) {
47404 throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
47407 for (var j = 0; j < ring[ring.length - 1].length; j++) {
47408 // Check if first point of Polygon contains two numbers
47409 if (ring[ring.length - 1][j] !== ring[0][j]) {
47410 throw new Error("First and last Position are not equivalent.");
47417 coordinates: coordinates
47419 return feature(geom, properties, options);
47422 exports.polygon = polygon;
47424 * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
47427 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
47428 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47429 * @param {Object} [options={}] Optional Parameters
47430 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47431 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47432 * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
47434 * var polygons = turf.polygons([
47435 * [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
47436 * [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
47442 function polygons(coordinates, properties, options) {
47443 if (options === void 0) {
47447 return featureCollection(coordinates.map(function (coords) {
47448 return polygon(coords, properties);
47452 exports.polygons = polygons;
47454 * Creates a {@link LineString} {@link Feature} from an Array of Positions.
47457 * @param {Array<Array<number>>} coordinates an array of Positions
47458 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47459 * @param {Object} [options={}] Optional Parameters
47460 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47461 * @param {string|number} [options.id] Identifier associated with the Feature
47462 * @returns {Feature<LineString>} LineString Feature
47464 * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
47465 * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
47471 function lineString(coordinates, properties, options) {
47472 if (options === void 0) {
47476 if (coordinates.length < 2) {
47477 throw new Error("coordinates must be an array of two or more positions");
47481 type: "LineString",
47482 coordinates: coordinates
47484 return feature(geom, properties, options);
47487 exports.lineString = lineString;
47489 * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
47491 * @name lineStrings
47492 * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
47493 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47494 * @param {Object} [options={}] Optional Parameters
47495 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
47496 * associated with the FeatureCollection
47497 * @param {string|number} [options.id] Identifier associated with the FeatureCollection
47498 * @returns {FeatureCollection<LineString>} LineString FeatureCollection
47500 * var linestrings = turf.lineStrings([
47501 * [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
47502 * [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
47508 function lineStrings(coordinates, properties, options) {
47509 if (options === void 0) {
47513 return featureCollection(coordinates.map(function (coords) {
47514 return lineString(coords, properties);
47518 exports.lineStrings = lineStrings;
47520 * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
47522 * @name featureCollection
47523 * @param {Feature[]} features input features
47524 * @param {Object} [options={}] Optional Parameters
47525 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47526 * @param {string|number} [options.id] Identifier associated with the Feature
47527 * @returns {FeatureCollection} FeatureCollection of Features
47529 * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
47530 * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
47531 * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
47533 * var collection = turf.featureCollection([
47542 function featureCollection(features, options) {
47543 if (options === void 0) {
47548 type: "FeatureCollection"
47552 fc.id = options.id;
47555 if (options.bbox) {
47556 fc.bbox = options.bbox;
47559 fc.features = features;
47563 exports.featureCollection = featureCollection;
47565 * Creates a {@link Feature<MultiLineString>} based on a
47566 * coordinate array. Properties can be added optionally.
47568 * @name multiLineString
47569 * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
47570 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47571 * @param {Object} [options={}] Optional Parameters
47572 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47573 * @param {string|number} [options.id] Identifier associated with the Feature
47574 * @returns {Feature<MultiLineString>} a MultiLineString feature
47575 * @throws {Error} if no coordinates are passed
47577 * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
47582 function multiLineString(coordinates, properties, options) {
47583 if (options === void 0) {
47588 type: "MultiLineString",
47589 coordinates: coordinates
47591 return feature(geom, properties, options);
47594 exports.multiLineString = multiLineString;
47596 * Creates a {@link Feature<MultiPoint>} based on a
47597 * coordinate array. Properties can be added optionally.
47600 * @param {Array<Array<number>>} coordinates an array of Positions
47601 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47602 * @param {Object} [options={}] Optional Parameters
47603 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47604 * @param {string|number} [options.id] Identifier associated with the Feature
47605 * @returns {Feature<MultiPoint>} a MultiPoint feature
47606 * @throws {Error} if no coordinates are passed
47608 * var multiPt = turf.multiPoint([[0,0],[10,10]]);
47613 function multiPoint(coordinates, properties, options) {
47614 if (options === void 0) {
47619 type: "MultiPoint",
47620 coordinates: coordinates
47622 return feature(geom, properties, options);
47625 exports.multiPoint = multiPoint;
47627 * Creates a {@link Feature<MultiPolygon>} based on a
47628 * coordinate array. Properties can be added optionally.
47630 * @name multiPolygon
47631 * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
47632 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47633 * @param {Object} [options={}] Optional Parameters
47634 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47635 * @param {string|number} [options.id] Identifier associated with the Feature
47636 * @returns {Feature<MultiPolygon>} a multipolygon feature
47637 * @throws {Error} if no coordinates are passed
47639 * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
47645 function multiPolygon(coordinates, properties, options) {
47646 if (options === void 0) {
47651 type: "MultiPolygon",
47652 coordinates: coordinates
47654 return feature(geom, properties, options);
47657 exports.multiPolygon = multiPolygon;
47659 * Creates a {@link Feature<GeometryCollection>} based on a
47660 * coordinate array. Properties can be added optionally.
47662 * @name geometryCollection
47663 * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
47664 * @param {Object} [properties={}] an Object of key-value pairs to add as properties
47665 * @param {Object} [options={}] Optional Parameters
47666 * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
47667 * @param {string|number} [options.id] Identifier associated with the Feature
47668 * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
47670 * var pt = turf.geometry("Point", [100, 0]);
47671 * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
47672 * var collection = turf.geometryCollection([pt, line]);
47677 function geometryCollection(geometries, properties, options) {
47678 if (options === void 0) {
47683 type: "GeometryCollection",
47684 geometries: geometries
47686 return feature(geom, properties, options);
47689 exports.geometryCollection = geometryCollection;
47691 * Round number to precision
47693 * @param {number} num Number
47694 * @param {number} [precision=0] Precision
47695 * @returns {number} rounded number
47697 * turf.round(120.4321)
47700 * turf.round(120.4321, 2)
47704 function round(num, precision) {
47705 if (precision === void 0) {
47709 if (precision && !(precision >= 0)) {
47710 throw new Error("precision must be a positive number");
47713 var multiplier = Math.pow(10, precision || 0);
47714 return Math.round(num * multiplier) / multiplier;
47717 exports.round = round;
47719 * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
47720 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47722 * @name radiansToLength
47723 * @param {number} radians in radians across the sphere
47724 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47725 * meters, kilometres, kilometers.
47726 * @returns {number} distance
47729 function radiansToLength(radians, units) {
47730 if (units === void 0) {
47731 units = "kilometers";
47734 var factor = exports.factors[units];
47737 throw new Error(units + " units is invalid");
47740 return radians * factor;
47743 exports.radiansToLength = radiansToLength;
47745 * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
47746 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47748 * @name lengthToRadians
47749 * @param {number} distance in real units
47750 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47751 * meters, kilometres, kilometers.
47752 * @returns {number} radians
47755 function lengthToRadians(distance, units) {
47756 if (units === void 0) {
47757 units = "kilometers";
47760 var factor = exports.factors[units];
47763 throw new Error(units + " units is invalid");
47766 return distance / factor;
47769 exports.lengthToRadians = lengthToRadians;
47771 * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
47772 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
47774 * @name lengthToDegrees
47775 * @param {number} distance in real units
47776 * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
47777 * meters, kilometres, kilometers.
47778 * @returns {number} degrees
47781 function lengthToDegrees(distance, units) {
47782 return radiansToDegrees(lengthToRadians(distance, units));
47785 exports.lengthToDegrees = lengthToDegrees;
47787 * Converts any bearing angle from the north line direction (positive clockwise)
47788 * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
47790 * @name bearingToAzimuth
47791 * @param {number} bearing angle, between -180 and +180 degrees
47792 * @returns {number} angle between 0 and 360 degrees
47795 function bearingToAzimuth(bearing) {
47796 var angle = bearing % 360;
47805 exports.bearingToAzimuth = bearingToAzimuth;
47807 * Converts an angle in radians to degrees
47809 * @name radiansToDegrees
47810 * @param {number} radians angle in radians
47811 * @returns {number} degrees between 0 and 360 degrees
47814 function radiansToDegrees(radians) {
47815 var degrees = radians % (2 * Math.PI);
47816 return degrees * 180 / Math.PI;
47819 exports.radiansToDegrees = radiansToDegrees;
47821 * Converts an angle in degrees to radians
47823 * @name degreesToRadians
47824 * @param {number} degrees angle between 0 and 360 degrees
47825 * @returns {number} angle in radians
47828 function degreesToRadians(degrees) {
47829 var radians = degrees % 360;
47830 return radians * Math.PI / 180;
47833 exports.degreesToRadians = degreesToRadians;
47835 * Converts a length to the requested unit.
47836 * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
47838 * @param {number} length to be converted
47839 * @param {Units} [originalUnit="kilometers"] of the length
47840 * @param {Units} [finalUnit="kilometers"] returned unit
47841 * @returns {number} the converted length
47844 function convertLength(length, originalUnit, finalUnit) {
47845 if (originalUnit === void 0) {
47846 originalUnit = "kilometers";
47849 if (finalUnit === void 0) {
47850 finalUnit = "kilometers";
47853 if (!(length >= 0)) {
47854 throw new Error("length must be a positive number");
47857 return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
47860 exports.convertLength = convertLength;
47862 * Converts a area to the requested unit.
47863 * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
47864 * @param {number} area to be converted
47865 * @param {Units} [originalUnit="meters"] of the distance
47866 * @param {Units} [finalUnit="kilometers"] returned unit
47867 * @returns {number} the converted distance
47870 function convertArea(area, originalUnit, finalUnit) {
47871 if (originalUnit === void 0) {
47872 originalUnit = "meters";
47875 if (finalUnit === void 0) {
47876 finalUnit = "kilometers";
47879 if (!(area >= 0)) {
47880 throw new Error("area must be a positive number");
47883 var startFactor = exports.areaFactors[originalUnit];
47885 if (!startFactor) {
47886 throw new Error("invalid original units");
47889 var finalFactor = exports.areaFactors[finalUnit];
47891 if (!finalFactor) {
47892 throw new Error("invalid final units");
47895 return area / startFactor * finalFactor;
47898 exports.convertArea = convertArea;
47902 * @param {*} num Number to validate
47903 * @returns {boolean} true/false
47905 * turf.isNumber(123)
47907 * turf.isNumber('foo')
47911 function isNumber(num) {
47912 return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
47915 exports.isNumber = isNumber;
47919 * @param {*} input variable to validate
47920 * @returns {boolean} true/false
47922 * turf.isObject({elevation: 10})
47924 * turf.isObject('foo')
47928 function isObject(input) {
47929 return !!input && input.constructor === Object;
47932 exports.isObject = isObject;
47937 * @param {Array<number>} bbox BBox to validate
47939 * @throws Error if BBox is not valid
47941 * validateBBox([-180, -40, 110, 50])
47943 * validateBBox([-180, -40])
47945 * validateBBox('Foo')
47949 * validateBBox(null)
47951 * validateBBox(undefined)
47955 function validateBBox(bbox) {
47957 throw new Error("bbox is required");
47960 if (!Array.isArray(bbox)) {
47961 throw new Error("bbox must be an Array");
47964 if (bbox.length !== 4 && bbox.length !== 6) {
47965 throw new Error("bbox must be an Array of 4 or 6 numbers");
47968 bbox.forEach(function (num) {
47969 if (!isNumber(num)) {
47970 throw new Error("bbox must only contain numbers");
47975 exports.validateBBox = validateBBox;
47980 * @param {string|number} id Id to validate
47982 * @throws Error if Id is not valid
47984 * validateId([-180, -40, 110, 50])
47986 * validateId([-180, -40])
47988 * validateId('Foo')
47994 * validateId(undefined)
47998 function validateId(id) {
48000 throw new Error("id is required");
48003 if (["string", "number"].indexOf(_typeof(id)) === -1) {
48004 throw new Error("id must be a number or a string");
48008 exports.validateId = validateId; // Deprecated methods
48010 function radians2degrees() {
48011 throw new Error("method has been renamed to `radiansToDegrees`");
48014 exports.radians2degrees = radians2degrees;
48016 function degrees2radians() {
48017 throw new Error("method has been renamed to `degreesToRadians`");
48020 exports.degrees2radians = degrees2radians;
48022 function distanceToDegrees() {
48023 throw new Error("method has been renamed to `lengthToDegrees`");
48026 exports.distanceToDegrees = distanceToDegrees;
48028 function distanceToRadians() {
48029 throw new Error("method has been renamed to `lengthToRadians`");
48032 exports.distanceToRadians = distanceToRadians;
48034 function radiansToDistance() {
48035 throw new Error("method has been renamed to `radiansToLength`");
48038 exports.radiansToDistance = radiansToDistance;
48040 function bearingToAngle() {
48041 throw new Error("method has been renamed to `bearingToAzimuth`");
48044 exports.bearingToAngle = bearingToAngle;
48046 function convertDistance() {
48047 throw new Error("method has been renamed to `convertLength`");
48050 exports.convertDistance = convertDistance;
48053 var invariant = createCommonjsModule(function (module, exports) {
48055 Object.defineProperty(exports, "__esModule", {
48059 * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.
48062 * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers
48063 * @returns {Array<number>} coordinates
48065 * var pt = turf.point([10, 10]);
48067 * var coord = turf.getCoord(pt);
48071 function getCoord(coord) {
48073 throw new Error("coord is required");
48076 if (!Array.isArray(coord)) {
48077 if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
48078 return coord.geometry.coordinates;
48081 if (coord.type === "Point") {
48082 return coord.coordinates;
48086 if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
48090 throw new Error("coord must be GeoJSON Point or an Array of numbers");
48093 exports.getCoord = getCoord;
48095 * Unwrap coordinates from a Feature, Geometry Object or an Array
48098 * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array
48099 * @returns {Array<any>} coordinates
48101 * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]);
48103 * var coords = turf.getCoords(poly);
48104 * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]
48107 function getCoords(coords) {
48108 if (Array.isArray(coords)) {
48113 if (coords.type === "Feature") {
48114 if (coords.geometry !== null) {
48115 return coords.geometry.coordinates;
48119 if (coords.coordinates) {
48120 return coords.coordinates;
48124 throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
48127 exports.getCoords = getCoords;
48129 * Checks if coordinates contains a number
48131 * @name containsNumber
48132 * @param {Array<any>} coordinates GeoJSON Coordinates
48133 * @returns {boolean} true if Array contains a number
48136 function containsNumber(coordinates) {
48137 if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) {
48141 if (Array.isArray(coordinates[0]) && coordinates[0].length) {
48142 return containsNumber(coordinates[0]);
48145 throw new Error("coordinates must only contain numbers");
48148 exports.containsNumber = containsNumber;
48150 * Enforce expectations about types of GeoJSON objects for Turf.
48152 * @name geojsonType
48153 * @param {GeoJSON} value any GeoJSON object
48154 * @param {string} type expected GeoJSON type
48155 * @param {string} name name of calling function
48156 * @throws {Error} if value is not the expected type.
48159 function geojsonType(value, type, name) {
48160 if (!type || !name) {
48161 throw new Error("type and name required");
48164 if (!value || value.type !== type) {
48165 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type);
48169 exports.geojsonType = geojsonType;
48171 * Enforce expectations about types of {@link Feature} inputs for Turf.
48172 * Internally this uses {@link geojsonType} to judge geometry types.
48175 * @param {Feature} feature a feature with an expected geometry type
48176 * @param {string} type expected GeoJSON type
48177 * @param {string} name name of calling function
48178 * @throws {Error} error if value is not the expected type.
48181 function featureOf(feature, type, name) {
48183 throw new Error("No feature passed");
48187 throw new Error(".featureOf() requires a name");
48190 if (!feature || feature.type !== "Feature" || !feature.geometry) {
48191 throw new Error("Invalid input to " + name + ", Feature with geometry required");
48194 if (!feature.geometry || feature.geometry.type !== type) {
48195 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
48199 exports.featureOf = featureOf;
48201 * Enforce expectations about types of {@link FeatureCollection} inputs for Turf.
48202 * Internally this uses {@link geojsonType} to judge geometry types.
48204 * @name collectionOf
48205 * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged
48206 * @param {string} type expected GeoJSON type
48207 * @param {string} name name of calling function
48208 * @throws {Error} if value is not the expected type.
48211 function collectionOf(featureCollection, type, name) {
48212 if (!featureCollection) {
48213 throw new Error("No featureCollection passed");
48217 throw new Error(".collectionOf() requires a name");
48220 if (!featureCollection || featureCollection.type !== "FeatureCollection") {
48221 throw new Error("Invalid input to " + name + ", FeatureCollection required");
48224 for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
48225 var feature = _a[_i];
48227 if (!feature || feature.type !== "Feature" || !feature.geometry) {
48228 throw new Error("Invalid input to " + name + ", Feature with geometry required");
48231 if (!feature.geometry || feature.geometry.type !== type) {
48232 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
48237 exports.collectionOf = collectionOf;
48239 * Get Geometry from Feature or Geometry Object
48241 * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
48242 * @returns {Geometry|null} GeoJSON Geometry Object
48243 * @throws {Error} if geojson is not a Feature or Geometry Object
48246 * "type": "Feature",
48247 * "properties": {},
48250 * "coordinates": [110, 40]
48253 * var geom = turf.getGeom(point)
48254 * //={"type": "Point", "coordinates": [110, 40]}
48257 function getGeom(geojson) {
48258 if (geojson.type === "Feature") {
48259 return geojson.geometry;
48265 exports.getGeom = getGeom;
48267 * Get GeoJSON object's type, Geometry type is prioritize.
48269 * @param {GeoJSON} geojson GeoJSON object
48270 * @param {string} [name="geojson"] name of the variable to display in error message
48271 * @returns {string} GeoJSON type
48274 * "type": "Feature",
48275 * "properties": {},
48278 * "coordinates": [110, 40]
48281 * var geom = turf.getType(point)
48285 function getType(geojson, name) {
48286 if (geojson.type === "FeatureCollection") {
48287 return "FeatureCollection";
48290 if (geojson.type === "GeometryCollection") {
48291 return "GeometryCollection";
48294 if (geojson.type === "Feature" && geojson.geometry !== null) {
48295 return geojson.geometry.type;
48298 return geojson.type;
48301 exports.getType = getType;
48304 var lineclip_1 = lineclip;
48305 var _default = lineclip;
48306 lineclip.polyline = lineclip;
48307 lineclip.polygon = polygonclip; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
48308 // handle polylines rather than just segments
48310 function lineclip(points, bbox, result) {
48311 var len = points.length,
48312 codeA = bitCode(points[0], bbox),
48319 if (!result) result = [];
48321 for (i = 1; i < len; i++) {
48324 codeB = lastCode = bitCode(b, bbox);
48327 if (!(codeA | codeB)) {
48331 if (codeB !== lastCode) {
48332 // segment went outside
48336 // start a new line
48340 } else if (i === len - 1) {
48345 } else if (codeA & codeB) {
48348 } else if (codeA) {
48349 // a outside, intersect with clip edge
48350 a = intersect(a, b, codeA, bbox);
48351 codeA = bitCode(a, bbox);
48354 b = intersect(a, b, codeB, bbox);
48355 codeB = bitCode(b, bbox);
48362 if (part.length) result.push(part);
48364 } // Sutherland-Hodgeman polygon clipping algorithm
48367 function polygonclip(points, bbox) {
48368 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
48370 for (edge = 1; edge <= 8; edge *= 2) {
48372 prev = points[points.length - 1];
48373 prevInside = !(bitCode(prev, bbox) & edge);
48375 for (i = 0; i < points.length; i++) {
48377 inside = !(bitCode(p, bbox) & edge); // if segment goes through the clip window, add an intersection
48379 if (inside !== prevInside) result.push(intersect(prev, p, edge, bbox));
48380 if (inside) result.push(p); // add a point if it's inside
48383 prevInside = inside;
48387 if (!points.length) break;
48391 } // intersect a segment against one of the 4 lines that make up the bbox
48394 function intersect(a, b, edge, bbox) {
48395 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
48396 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
48397 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
48398 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
48400 } // bit code reflects the point position relative to the bbox:
48402 // top 1001 1000 1010
48403 // mid 0001 0000 0010
48404 // bottom 0101 0100 0110
48407 function bitCode(p, bbox) {
48409 if (p[0] < bbox[0]) code |= 1; // left
48410 else if (p[0] > bbox[2]) code |= 2; // right
48412 if (p[1] < bbox[1]) code |= 4; // bottom
48413 else if (p[1] > bbox[3]) code |= 8; // top
48417 lineclip_1["default"] = _default;
48419 var bboxClip_1 = createCommonjsModule(function (module, exports) {
48421 var __importStar = commonjsGlobal && commonjsGlobal.__importStar || function (mod) {
48422 if (mod && mod.__esModule) return mod;
48424 if (mod != null) for (var k in mod) {
48425 if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
48427 result["default"] = mod;
48431 Object.defineProperty(exports, "__esModule", {
48435 var lineclip = __importStar(lineclip_1);
48437 * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
48438 * [lineclip](https://github.com/mapbox/lineclip).
48439 * May result in degenerate edges when clipping Polygons.
48442 * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
48443 * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
48444 * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
48446 * var bbox = [0, 0, 10, 10];
48447 * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
48449 * var clipped = turf.bboxClip(poly, bbox);
48452 * var addToMap = [bbox, poly, clipped]
48456 function bboxClip(feature, bbox) {
48457 var geom = invariant.getGeom(feature);
48458 var type = geom.type;
48459 var properties = feature.type === "Feature" ? feature.properties : {};
48460 var coords = geom.coordinates;
48464 case "MultiLineString":
48467 if (type === "LineString") {
48471 coords.forEach(function (line) {
48472 lineclip.polyline(line, bbox, lines_1);
48475 if (lines_1.length === 1) {
48476 return helpers$1.lineString(lines_1[0], properties);
48479 return helpers$1.multiLineString(lines_1, properties);
48482 return helpers$1.polygon(clipPolygon(coords, bbox), properties);
48484 case "MultiPolygon":
48485 return helpers$1.multiPolygon(coords.map(function (poly) {
48486 return clipPolygon(poly, bbox);
48490 throw new Error("geometry " + type + " not supported");
48494 exports["default"] = bboxClip;
48496 function clipPolygon(rings, bbox) {
48499 for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
48500 var ring = rings_1[_i];
48501 var clipped = lineclip.polygon(ring, bbox);
48503 if (clipped.length > 0) {
48504 if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
48505 clipped.push(clipped[0]);
48508 if (clipped.length >= 4) {
48509 outRings.push(clipped);
48517 var turf_bboxClip = /*@__PURE__*/getDefaultExportFromCjs(bboxClip_1);
48519 var fastJsonStableStringify = function fastJsonStableStringify(data, opts) {
48520 if (!opts) opts = {};
48521 if (typeof opts === 'function') opts = {
48524 var cycles = typeof opts.cycles === 'boolean' ? opts.cycles : false;
48526 var cmp = opts.cmp && function (f) {
48527 return function (node) {
48528 return function (a, b) {
48537 return f(aobj, bobj);
48543 return function stringify(node) {
48544 if (node && node.toJSON && typeof node.toJSON === 'function') {
48545 node = node.toJSON();
48548 if (node === undefined) return;
48549 if (typeof node == 'number') return isFinite(node) ? '' + node : 'null';
48550 if (_typeof(node) !== 'object') return JSON.stringify(node);
48553 if (Array.isArray(node)) {
48556 for (i = 0; i < node.length; i++) {
48558 out += stringify(node[i]) || 'null';
48564 if (node === null) return 'null';
48566 if (seen.indexOf(node) !== -1) {
48567 if (cycles) return JSON.stringify('__cycle__');
48568 throw new TypeError('Converting circular structure to JSON');
48571 var seenIndex = seen.push(node) - 1;
48572 var keys = Object.keys(node).sort(cmp && cmp(node));
48575 for (i = 0; i < keys.length; i++) {
48577 var value = stringify(node[key]);
48578 if (!value) continue;
48579 if (out) out += ',';
48580 out += JSON.stringify(key) + ':' + value;
48583 seen.splice(seenIndex, 1);
48584 return '{' + out + '}';
48588 function DEFAULT_COMPARE(a, b) {
48589 return a > b ? 1 : a < b ? -1 : 0;
48592 var SplayTree = /*#__PURE__*/function () {
48593 function SplayTree() {
48594 var compare = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_COMPARE;
48595 var noDuplicates = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
48597 _classCallCheck(this, SplayTree);
48599 this._compare = compare;
48602 this._noDuplicates = !!noDuplicates;
48605 _createClass(SplayTree, [{
48607 value: function rotateLeft(x) {
48612 if (y.left) y.left.parent = x;
48613 y.parent = x.parent;
48616 if (!x.parent) this._root = y;else if (x === x.parent.left) x.parent.left = y;else x.parent.right = y;
48621 key: "rotateRight",
48622 value: function rotateRight(x) {
48627 if (y.right) y.right.parent = x;
48628 y.parent = x.parent;
48631 if (!x.parent) this._root = y;else if (x === x.parent.left) x.parent.left = y;else x.parent.right = y;
48632 if (y) y.right = x;
48637 value: function _splay(x) {
48642 if (p.left === x) this.rotateRight(p);else this.rotateLeft(p);
48643 } else if (p.left === x && p.parent.left === p) {
48644 this.rotateRight(p.parent);
48645 this.rotateRight(p);
48646 } else if (p.right === x && p.parent.right === p) {
48647 this.rotateLeft(p.parent);
48648 this.rotateLeft(p);
48649 } else if (p.left === x && p.parent.right === p) {
48650 this.rotateRight(p);
48651 this.rotateLeft(p);
48653 this.rotateLeft(p);
48654 this.rotateRight(p);
48660 value: function splay(x) {
48661 var p, gp, ggp, l, r;
48667 if (gp && gp.parent) {
48669 if (ggp.left === gp) ggp.left = x;else ggp.right = x;
48679 if (x === p.left) {
48682 if (gp.left === p) {
48686 gp.left.parent = gp;
48687 } else gp.left = null;
48696 } else gp.right = null;
48706 } else p.left = null;
48713 if (gp.right === p) {
48717 gp.right.parent = gp;
48718 } else gp.right = null;
48727 } else gp.left = null;
48737 } else p.right = null;
48746 value: function replace(u, v) {
48747 if (!u.parent) this._root = v;else if (u === u.parent.left) u.parent.left = v;else u.parent.right = v;
48748 if (v) v.parent = u.parent;
48752 value: function minNode() {
48753 var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
48754 if (u) while (u.left) {
48761 value: function maxNode() {
48762 var u = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
48763 if (u) while (u.right) {
48770 value: function insert(key, data) {
48771 var z = this._root;
48773 var comp = this._compare;
48776 if (this._noDuplicates) {
48779 cmp = comp(z.key, key);
48780 if (cmp === 0) return;else if (comp(z.key, key) < 0) z = z.right;else z = z.left;
48785 if (comp(z.key, key) < 0) z = z.right;else z = z.left;
48796 if (!p) this._root = z;else if (comp(p.key, z.key) < 0) p.right = z;else p.left = z;
48803 value: function find(key) {
48804 var z = this._root;
48805 var comp = this._compare;
48808 var cmp = comp(z.key, key);
48809 if (cmp < 0) z = z.right;else if (cmp > 0) z = z.left;else return z;
48815 * Whether the tree contains a node with the given key
48817 * @return {boolean} true/false
48822 value: function contains(key) {
48823 var node = this._root;
48824 var comparator = this._compare;
48827 var cmp = comparator(key, node.key);
48828 if (cmp === 0) return true;else if (cmp < 0) node = node.left;else node = node.right;
48835 value: function remove(key) {
48836 var z = this.find(key);
48837 if (!z) return false;
48839 if (!z.left) this.replace(z, z.right);else if (!z.right) this.replace(z, z.left);else {
48840 var y = this.minNode(z.right);
48842 if (y.parent !== z) {
48843 this.replace(y, y.right);
48845 y.right.parent = y;
48848 this.replace(z, y);
48857 value: function removeNode(z) {
48858 if (!z) return false;
48860 if (!z.left) this.replace(z, z.right);else if (!z.right) this.replace(z, z.left);else {
48861 var y = this.minNode(z.right);
48863 if (y.parent !== z) {
48864 this.replace(y, y.right);
48866 y.right.parent = y;
48869 this.replace(z, y);
48878 value: function erase(key) {
48879 var z = this.find(key);
48888 sMax = this.maxNode(s);
48894 if (s) sMax.right = t;else this._root = t;
48901 * Removes and returns the node with smallest key
48907 value: function pop() {
48908 var node = this._root,
48909 returnValue = null;
48912 while (node.left) {
48920 this.remove(node.key);
48923 return returnValue;
48925 /* eslint-disable class-methods-use-this */
48929 * @param {Node} node
48935 value: function next(node) {
48936 var successor = node;
48939 if (successor.right) {
48940 successor = successor.right;
48942 while (successor && successor.left) {
48943 successor = successor.left;
48946 successor = node.parent;
48948 while (successor && successor.right === node) {
48950 successor = successor.parent;
48959 * @param {Node} node
48965 value: function prev(node) {
48966 var predecessor = node;
48969 if (predecessor.left) {
48970 predecessor = predecessor.left;
48972 while (predecessor && predecessor.right) {
48973 predecessor = predecessor.right;
48976 predecessor = node.parent;
48978 while (predecessor && predecessor.left === node) {
48979 node = predecessor;
48980 predecessor = predecessor.parent;
48985 return predecessor;
48987 /* eslint-enable class-methods-use-this */
48990 * @param {forEachCallback} callback
48991 * @return {SplayTree}
48996 value: function forEach(callback) {
48997 var current = this._root;
49003 // Reach the left most Node of the current Node
49005 // Place pointer to a tree node on the stack
49006 // before traversing the node's left subtree
49008 current = current.left;
49010 // BackTrack from the empty subtree and visit the Node
49011 // at the top of the stack; however, if the stack is
49012 // empty you are done
49013 if (s.length > 0) {
49015 callback(current, i++); // We have visited the node and its left
49016 // subtree. Now, it's right subtree's turn
49018 current = current.right;
49019 } else done = true;
49026 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
49028 * @param {Key} high
49029 * @param {Function} fn
49031 * @return {SplayTree}
49036 value: function range(low, high, fn, ctx) {
49038 var compare = this._compare;
49039 var node = this._root,
49042 while (Q.length !== 0 || node) {
49048 cmp = compare(node.key, high);
49052 } else if (compare(node.key, low) >= 0) {
49053 if (fn.call(ctx, node)) return this; // stop if smth is returned
49063 * Returns all keys in order
49064 * @return {Array<Key>}
49069 value: function keys() {
49070 var current = this._root;
49078 current = current.left;
49080 if (s.length > 0) {
49082 r.push(current.key);
49083 current = current.right;
49084 } else done = true;
49091 * Returns `data` fields of all nodes in order.
49092 * @return {Array<Value>}
49097 value: function values() {
49098 var current = this._root;
49106 current = current.left;
49108 if (s.length > 0) {
49110 r.push(current.data);
49111 current = current.right;
49112 } else done = true;
49119 * Returns node at given index
49120 * @param {number} index
49126 value: function at(index) {
49127 // removed after a consideration, more misleading than useful
49128 // index = index % this.size;
49129 // if (index < 0) index = this.size - index;
49130 var current = this._root;
49138 current = current.left;
49140 if (s.length > 0) {
49142 if (i === index) return current;
49144 current = current.right;
49145 } else done = true;
49152 * Bulk-load items. Both array have to be same size
49153 * @param {Array<Key>} keys
49154 * @param {Array<Value>} [values]
49155 * @param {Boolean} [presort=false] Pre-sort keys and values, using
49156 * tree's comparator. Sorting is done
49158 * @return {AVLTree}
49163 value: function load() {
49164 var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
49165 var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
49166 var presort = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
49167 if (this._size !== 0) throw new Error('bulk-load: tree is not empty');
49168 var size = keys.length;
49169 if (presort) sort(keys, values, 0, size - 1, this._compare);
49170 this._root = loadRecursive(null, keys, values, 0, size);
49176 value: function min() {
49177 var node = this.minNode(this._root);
49178 if (node) return node.key;else return null;
49182 value: function max() {
49183 var node = this.maxNode(this._root);
49184 if (node) return node.key;else return null;
49188 value: function isEmpty() {
49189 return this._root === null;
49193 get: function get() {
49197 * Create a tree and load it with items
49198 * @param {Array<Key>} keys
49199 * @param {Array<Value>?} [values]
49200 * @param {Function?} [comparator]
49201 * @param {Boolean?} [presort=false] Pre-sort keys and values, using
49202 * tree's comparator. Sorting is done
49204 * @param {Boolean?} [noDuplicates=false] Allow duplicates
49205 * @return {SplayTree}
49210 value: function createTree(keys, values, comparator, presort, noDuplicates) {
49211 return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
49218 function loadRecursive(parent, keys, values, start, end) {
49219 var size = end - start;
49222 var middle = start + Math.floor(size / 2);
49223 var key = keys[middle];
49224 var data = values[middle];
49230 node.left = loadRecursive(node, keys, values, start, middle);
49231 node.right = loadRecursive(node, keys, values, middle + 1, end);
49238 function sort(keys, values, left, right, compare) {
49239 if (left >= right) return;
49240 var pivot = keys[left + right >> 1];
49247 } while (compare(keys[i], pivot) < 0);
49251 } while (compare(keys[j], pivot) > 0);
49258 values[i] = values[j];
49262 sort(keys, values, left, j, compare);
49263 sort(keys, values, j + 1, right, compare);
49267 var NON_CONTRIBUTING = 1;
49268 var SAME_TRANSITION = 2;
49269 var DIFFERENT_TRANSITION = 3;
49271 var INTERSECTION = 0;
49273 var DIFFERENCE = 2;
49277 * @param {SweepEvent} event
49278 * @param {SweepEvent} prev
49279 * @param {Operation} operation
49282 function computeFields(event, prev, operation) {
49283 // compute inOut and otherInOut fields
49284 if (prev === null) {
49285 event.inOut = false;
49286 event.otherInOut = true; // previous line segment in sweepline belongs to the same polygon
49288 if (event.isSubject === prev.isSubject) {
49289 event.inOut = !prev.inOut;
49290 event.otherInOut = prev.otherInOut; // previous line segment in sweepline belongs to the clipping polygon
49292 event.inOut = !prev.otherInOut;
49293 event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
49294 } // compute prevInResult field
49298 event.prevInResult = !inResult(prev, operation) || prev.isVertical() ? prev.prevInResult : prev;
49300 } // check if the line segment belongs to the Boolean operation
49303 var isInResult = inResult(event, operation);
49306 event.resultTransition = determineResultTransition(event, operation);
49308 event.resultTransition = 0;
49311 /* eslint-disable indent */
49313 function inResult(event, operation) {
49314 switch (event.type) {
49316 switch (operation) {
49318 return !event.otherInOut;
49321 return event.otherInOut;
49324 // return (event.isSubject && !event.otherInOut) ||
49325 // (!event.isSubject && event.otherInOut);
49326 return event.isSubject && event.otherInOut || !event.isSubject && !event.otherInOut;
49334 case SAME_TRANSITION:
49335 return operation === INTERSECTION || operation === UNION;
49337 case DIFFERENT_TRANSITION:
49338 return operation === DIFFERENCE;
49340 case NON_CONTRIBUTING:
49346 /* eslint-enable indent */
49349 function determineResultTransition(event, operation) {
49350 var thisIn = !event.inOut;
49351 var thatIn = !event.otherInOut;
49354 switch (operation) {
49356 isIn = thisIn && thatIn;
49360 isIn = thisIn || thatIn;
49364 isIn = thisIn ^ thatIn;
49368 if (event.isSubject) {
49369 isIn = thisIn && !thatIn;
49371 isIn = thatIn && !thisIn;
49377 return isIn ? +1 : -1;
49380 var SweepEvent = /*#__PURE__*/function () {
49384 * @class {SweepEvent}
49385 * @param {Array.<Number>} point
49386 * @param {Boolean} left
49387 * @param {SweepEvent=} otherEvent
49388 * @param {Boolean} isSubject
49389 * @param {Number} edgeType
49391 function SweepEvent(point, left, otherEvent, isSubject, edgeType) {
49392 _classCallCheck(this, SweepEvent);
49395 * Is left endpoint?
49400 * @type {Array.<Number>}
49403 this.point = point;
49405 * Other edge reference
49406 * @type {SweepEvent}
49409 this.otherEvent = otherEvent;
49411 * Belongs to source or clipping polygon
49415 this.isSubject = isSubject;
49417 * Edge contribution type
49421 this.type = edgeType || NORMAL;
49423 * In-out transition for the sweepline crossing polygon
49427 this.inOut = false;
49432 this.otherInOut = false;
49434 * Previous event in result?
49435 * @type {SweepEvent}
49438 this.prevInResult = null;
49440 * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
49444 this.resultTransition = 0; // connection step
49450 this.otherPos = -1;
49455 this.outputContourId = -1;
49456 this.isExteriorRing = true; // TODO: Looks unused, remove?
49459 * @param {Array.<Number>} p
49460 * @return {Boolean}
49464 _createClass(SweepEvent, [{
49466 value: function isBelow(p) {
49467 var p0 = this.point,
49468 p1 = this.otherEvent.point;
49469 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 :
49470 : (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;
49473 * @param {Array.<Number>} p
49474 * @return {Boolean}
49479 value: function isAbove(p) {
49480 return !this.isBelow(p);
49483 * @return {Boolean}
49488 value: function isVertical() {
49489 return this.point[0] === this.otherEvent.point[0];
49492 * Does event belong to result?
49493 * @return {Boolean}
49498 value: function clone() {
49499 var copy = new SweepEvent(this.point, this.left, this.otherEvent, this.isSubject, this.type);
49500 copy.contourId = this.contourId;
49501 copy.resultTransition = this.resultTransition;
49502 copy.prevInResult = this.prevInResult;
49503 copy.isExteriorRing = this.isExteriorRing;
49504 copy.inOut = this.inOut;
49505 copy.otherInOut = this.otherInOut;
49510 get: function get() {
49511 return this.resultTransition !== 0;
49518 function equals(p1, p2) {
49519 if (p1[0] === p2[0]) {
49520 if (p1[1] === p2[1]) {
49528 } // const EPSILON = 1e-9;
49529 // const abs = Math.abs;
49530 // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
49531 // Precision problem.
49533 // module.exports = function equals(p1, p2) {
49534 // return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
49537 var epsilon$1 = 1.1102230246251565e-16;
49538 var splitter = 134217729;
49539 var resulterrbound = (3 + 8 * epsilon$1) * epsilon$1; // fast_expansion_sum_zeroelim routine from oritinal code
49541 function sum(elen, e, flen, f, h) {
49542 var Q, Qnew, hh, bvirt;
49548 if (fnow > enow === fnow > -enow) {
49550 enow = e[++eindex];
49553 fnow = f[++findex];
49558 if (eindex < elen && findex < flen) {
49559 if (fnow > enow === fnow > -enow) {
49561 hh = Q - (Qnew - enow);
49562 enow = e[++eindex];
49565 hh = Q - (Qnew - fnow);
49566 fnow = f[++findex];
49575 while (eindex < elen && findex < flen) {
49576 if (fnow > enow === fnow > -enow) {
49579 hh = Q - (Qnew - bvirt) + (enow - bvirt);
49580 enow = e[++eindex];
49584 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
49585 fnow = f[++findex];
49596 while (eindex < elen) {
49599 hh = Q - (Qnew - bvirt) + (enow - bvirt);
49600 enow = e[++eindex];
49608 while (findex < flen) {
49611 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
49612 fnow = f[++findex];
49620 if (Q !== 0 || hindex === 0) {
49626 function estimate(elen, e) {
49629 for (var i = 1; i < elen; i++) {
49636 return new Float64Array(n);
49639 var ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
49640 var ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
49641 var ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
49648 function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
49649 var acxtail, acytail, bcxtail, bcytail;
49651 var bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
49658 c = splitter * acx;
49659 ahi = c - (c - acx);
49661 c = splitter * bcy;
49662 bhi = c - (c - bcy);
49664 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49666 c = splitter * acy;
49667 ahi = c - (c - acy);
49669 c = splitter * bcx;
49670 bhi = c - (c - bcx);
49672 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49675 B[0] = s0 - (_i + bvirt) + (bvirt - t0);
49678 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49681 B[1] = _0 - (_i + bvirt) + (bvirt - t1);
49684 B[2] = _j - (u3 - bvirt) + (_i - bvirt);
49686 var det = estimate(4, B);
49687 var errbound = ccwerrboundB * detsum;
49689 if (det >= errbound || -det >= errbound) {
49694 acxtail = ax - (acx + bvirt) + (bvirt - cx);
49696 bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
49698 acytail = ay - (acy + bvirt) + (bvirt - cy);
49700 bcytail = by - (bcy + bvirt) + (bvirt - cy);
49702 if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
49706 errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
49707 det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail);
49708 if (det >= errbound || -det >= errbound) return det;
49709 s1 = acxtail * bcy;
49710 c = splitter * acxtail;
49711 ahi = c - (c - acxtail);
49712 alo = acxtail - ahi;
49713 c = splitter * bcy;
49714 bhi = c - (c - bcy);
49716 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49717 t1 = acytail * bcx;
49718 c = splitter * acytail;
49719 ahi = c - (c - acytail);
49720 alo = acytail - ahi;
49721 c = splitter * bcx;
49722 bhi = c - (c - bcx);
49724 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49727 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49730 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49733 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49736 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49738 var C1len = sum(4, B, 4, u, C1);
49739 s1 = acx * bcytail;
49740 c = splitter * acx;
49741 ahi = c - (c - acx);
49743 c = splitter * bcytail;
49744 bhi = c - (c - bcytail);
49745 blo = bcytail - bhi;
49746 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49747 t1 = acy * bcxtail;
49748 c = splitter * acy;
49749 ahi = c - (c - acy);
49751 c = splitter * bcxtail;
49752 bhi = c - (c - bcxtail);
49753 blo = bcxtail - bhi;
49754 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49757 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49760 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49763 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49766 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49768 var C2len = sum(C1len, C1, 4, u, C2);
49769 s1 = acxtail * bcytail;
49770 c = splitter * acxtail;
49771 ahi = c - (c - acxtail);
49772 alo = acxtail - ahi;
49773 c = splitter * bcytail;
49774 bhi = c - (c - bcytail);
49775 blo = bcytail - bhi;
49776 s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
49777 t1 = acytail * bcxtail;
49778 c = splitter * acytail;
49779 ahi = c - (c - acytail);
49780 alo = acytail - ahi;
49781 c = splitter * bcxtail;
49782 bhi = c - (c - bcxtail);
49783 blo = bcxtail - bhi;
49784 t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
49787 u[0] = s0 - (_i + bvirt) + (bvirt - t0);
49790 _0 = s1 - (_j - bvirt) + (_i - bvirt);
49793 u[1] = _0 - (_i + bvirt) + (bvirt - t1);
49796 u[2] = _j - (u3 - bvirt) + (_i - bvirt);
49798 var Dlen = sum(C2len, C2, 4, u, D);
49799 return D[Dlen - 1];
49802 function orient2d(ax, ay, bx, by, cx, cy) {
49803 var detleft = (ay - cy) * (bx - cx);
49804 var detright = (ax - cx) * (by - cy);
49805 var det = detleft - detright;
49806 if (detleft === 0 || detright === 0 || detleft > 0 !== detright > 0) return det;
49807 var detsum = Math.abs(detleft + detright);
49808 if (Math.abs(det) >= ccwerrboundA * detsum) return det;
49809 return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
49813 * Signed area of the triangle (p0, p1, p2)
49814 * @param {Array.<Number>} p0
49815 * @param {Array.<Number>} p1
49816 * @param {Array.<Number>} p2
49820 function signedArea(p0, p1, p2) {
49821 var res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
49822 if (res > 0) return -1;
49823 if (res < 0) return 1;
49828 * @param {SweepEvent} e1
49829 * @param {SweepEvent} e2
49833 function compareEvents(e1, e2) {
49835 var p2 = e2.point; // Different x-coordinate
49837 if (p1[0] > p2[0]) return 1;
49838 if (p1[0] < p2[0]) return -1; // Different points, but same x-coordinate
49839 // Event with lower y-coordinate is processed first
49841 if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1;
49842 return specialCases(e1, e2, p1);
49844 /* eslint-disable no-unused-vars */
49846 function specialCases(e1, e2, p1, p2) {
49847 // Same coordinates, but one is a left endpoint and the other is
49848 // a right endpoint. The right endpoint is processed first
49849 if (e1.left !== e2.left) return e1.left ? 1 : -1; // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
49850 // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
49851 // Same coordinates, both events
49852 // are left endpoints or right endpoints.
49855 if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
49856 // the event associate to the bottom segment is processed first
49857 return !e1.isBelow(e2.otherEvent.point) ? 1 : -1;
49860 return !e1.isSubject && e2.isSubject ? 1 : -1;
49862 /* eslint-enable no-unused-vars */
49865 * @param {SweepEvent} se
49866 * @param {Array.<Number>} p
49867 * @param {Queue} queue
49871 function divideSegment(se, p, queue) {
49872 var r = new SweepEvent(p, false, se, se.isSubject);
49873 var l = new SweepEvent(p, true, se.otherEvent, se.isSubject);
49874 /* eslint-disable no-console */
49876 if (equals(se.point, se.otherEvent.point)) {
49877 console.warn('what is that, a collapsed segment?', se);
49879 /* eslint-enable no-console */
49882 r.contourId = l.contourId = se.contourId; // avoid a rounding error. The left event would be processed after the right event
49884 if (compareEvents(l, se.otherEvent) > 0) {
49885 se.otherEvent.left = true;
49887 } // avoid a rounding error. The left event would be processed after the right event
49888 // if (compareEvents(se, r) > 0) {}
49891 se.otherEvent.otherEvent = l;
49898 //const EPS = 1e-9;
49901 * Finds the magnitude of the cross product of two vectors (if we pretend
49902 * they're in three dimensions)
49904 * @param {Object} a First vector
49905 * @param {Object} b Second vector
49907 * @returns {Number} The magnitude of the cross product
49909 function crossProduct(a, b) {
49910 return a[0] * b[1] - a[1] * b[0];
49913 * Finds the dot product of two vectors.
49915 * @param {Object} a First vector
49916 * @param {Object} b Second vector
49918 * @returns {Number} The dot product
49922 function dotProduct(a, b) {
49923 return a[0] * b[0] + a[1] * b[1];
49926 * Finds the intersection (if any) between two line segments a and b, given the
49927 * line segments' end points a1, a2 and b1, b2.
49929 * This algorithm is based on Schneider and Eberly.
49930 * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
49933 * @param {Array.<Number>} a1 point of first line
49934 * @param {Array.<Number>} a2 point of first line
49935 * @param {Array.<Number>} b1 point of second line
49936 * @param {Array.<Number>} b2 point of second line
49937 * @param {Boolean=} noEndpointTouch whether to skip single touchpoints
49938 * (meaning connected segments) as
49940 * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
49941 * intersection. If they overlap, the two end points of the overlapping segment.
49946 function intersection (a1, a2, b1, b2, noEndpointTouch) {
49947 // The algorithm expects our lines in the form P + sd, where P is a point,
49948 // s is on the interval [0, 1], and d is a vector.
49949 // We are passed two points. P can be the first point of each pair. The
49950 // vector, then, could be thought of as the distance (in x and y components)
49951 // from the first point to the second point.
49952 // So first, let's make our vectors:
49953 var va = [a2[0] - a1[0], a2[1] - a1[1]];
49954 var vb = [b2[0] - b1[0], b2[1] - b1[1]]; // We also define a function to convert back to regular point form:
49956 /* eslint-disable arrow-body-style */
49958 function toPoint(p, s, d) {
49959 return [p[0] + s * d[0], p[1] + s * d[1]];
49961 /* eslint-enable arrow-body-style */
49962 // The rest is pretty much a straight port of the algorithm.
49965 var e = [b1[0] - a1[0], b1[1] - a1[1]];
49966 var kross = crossProduct(va, vb);
49967 var sqrKross = kross * kross;
49968 var sqrLenA = dotProduct(va, va); //const sqrLenB = dotProduct(vb, vb);
49969 // Check for line intersection. This works because of the properties of the
49970 // cross product -- specifically, two vectors are parallel if and only if the
49971 // cross product is the 0 vector. The full calculation involves relative error
49972 // to account for possible very small line segments. See Schneider & Eberly
49976 /* EPS * sqrLenB * sqLenA */
49978 // If they're not parallel, then (because these are line segments) they
49979 // still might not actually intersect. This code checks that the
49980 // intersection point of the lines is actually on both line segments.
49981 var s = crossProduct(e, vb) / kross;
49983 if (s < 0 || s > 1) {
49984 // not on line segment a
49988 var t = crossProduct(e, va) / kross;
49990 if (t < 0 || t > 1) {
49991 // not on line segment b
49995 if (s === 0 || s === 1) {
49996 // on an endpoint of line segment a
49997 return noEndpointTouch ? null : [toPoint(a1, s, va)];
50000 if (t === 0 || t === 1) {
50001 // on an endpoint of line segment b
50002 return noEndpointTouch ? null : [toPoint(b1, t, vb)];
50005 return [toPoint(a1, s, va)];
50006 } // If we've reached this point, then the lines are either parallel or the
50007 // same, but the segments could overlap partially or fully, or not at all.
50008 // So we need to find the overlap, if any. To do that, we can use e, which is
50009 // the (vector) difference between the two initial points. If this is parallel
50010 // with the line itself, then the two lines are the same line, and there will
50012 //const sqrLenE = dotProduct(e, e);
50015 kross = crossProduct(e, va);
50016 sqrKross = kross * kross;
50019 /* EPS * sqLenB * sqLenE */
50021 // Lines are just parallel, not the same. No overlap.
50025 var sa = dotProduct(va, e) / sqrLenA;
50026 var sb = sa + dotProduct(va, vb) / sqrLenA;
50027 var smin = Math.min(sa, sb);
50028 var smax = Math.max(sa, sb); // this is, essentially, the FindIntersection acting on floats from
50029 // Schneider & Eberly, just inlined into this function.
50031 if (smin <= 1 && smax >= 0) {
50032 // overlap on an end point
50034 return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
50038 return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
50041 if (noEndpointTouch && smin === 0 && smax === 1) return null; // There's overlap on a segment -- two points of intersection. Return both.
50043 return [toPoint(a1, smin > 0 ? smin : 0, va), toPoint(a1, smax < 1 ? smax : 1, va)];
50050 * @param {SweepEvent} se1
50051 * @param {SweepEvent} se2
50052 * @param {Queue} queue
50056 function possibleIntersection(se1, se2, queue) {
50057 // that disallows self-intersecting polygons,
50058 // did cost us half a day, so I'll leave it
50060 // if (se1.isSubject === se2.isSubject) return;
50061 var inter = intersection(se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
50062 var nintersections = inter ? inter.length : 0;
50063 if (nintersections === 0) return 0; // no intersection
50064 // the line segments intersect at an endpoint of both line segments
50066 if (nintersections === 1 && (equals(se1.point, se2.point) || equals(se1.otherEvent.point, se2.otherEvent.point))) {
50070 if (nintersections === 2 && se1.isSubject === se2.isSubject) {
50071 // if(se1.contourId === se2.contourId){
50072 // console.warn('Edges of the same polygon overlap',
50073 // se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
50075 //throw new Error('Edges of the same polygon overlap');
50077 } // The line segments associated to se1 and se2 intersect
50080 if (nintersections === 1) {
50081 // if the intersection point is not an endpoint of se1
50082 if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
50083 divideSegment(se1, inter[0], queue);
50084 } // if the intersection point is not an endpoint of se2
50087 if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
50088 divideSegment(se2, inter[0], queue);
50092 } // The line segments associated to se1 and se2 overlap
50096 var leftCoincide = false;
50097 var rightCoincide = false;
50099 if (equals(se1.point, se2.point)) {
50100 leftCoincide = true; // linked
50101 } else if (compareEvents(se1, se2) === 1) {
50102 events.push(se2, se1);
50104 events.push(se1, se2);
50107 if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
50108 rightCoincide = true;
50109 } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
50110 events.push(se2.otherEvent, se1.otherEvent);
50112 events.push(se1.otherEvent, se2.otherEvent);
50115 if (leftCoincide && rightCoincide || leftCoincide) {
50116 // both line segments are equal or share the left endpoint
50117 se2.type = NON_CONTRIBUTING;
50118 se1.type = se2.inOut === se1.inOut ? SAME_TRANSITION : DIFFERENT_TRANSITION;
50120 if (leftCoincide && !rightCoincide) {
50121 // honestly no idea, but changing events selection from [2, 1]
50122 // to [0, 1] fixes the overlapping self-intersecting polygons issue
50123 divideSegment(events[1].otherEvent, events[0].point, queue);
50127 } // the line segments share the right endpoint
50130 if (rightCoincide) {
50131 divideSegment(events[0], events[1].point, queue);
50133 } // no line segment includes totally the other one
50136 if (events[0] !== events[3].otherEvent) {
50137 divideSegment(events[0], events[1].point, queue);
50138 divideSegment(events[1], events[2].point, queue);
50140 } // one line segment includes the other one
50143 divideSegment(events[0], events[1].point, queue);
50144 divideSegment(events[3].otherEvent, events[2].point, queue);
50149 * @param {SweepEvent} le1
50150 * @param {SweepEvent} le2
50154 function compareSegments(le1, le2) {
50155 if (le1 === le2) return 0; // Segments are not collinear
50157 if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 || signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
50158 // If they share their left endpoint use the right endpoint to sort
50159 if (equals(le1.point, le2.point)) return le1.isBelow(le2.otherEvent.point) ? -1 : 1; // Different left endpoint: use the left endpoint to sort
50161 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
50162 // into S after the line segment associated to e2 ?
50164 if (compareEvents(le1, le2) === 1) return le2.isAbove(le1.point) ? -1 : 1; // The line segment associated to e2 has been inserted
50165 // into S after the line segment associated to e1
50167 return le1.isBelow(le2.point) ? -1 : 1;
50170 if (le1.isSubject === le2.isSubject) {
50172 var p1 = le1.point,
50175 if (p1[0] === p2[0] && p1[1] === p2[1]
50176 /*equals(le1.point, le2.point)*/
50178 p1 = le1.otherEvent.point;
50179 p2 = le2.otherEvent.point;
50180 if (p1[0] === p2[0] && p1[1] === p2[1]) return 0;else return le1.contourId > le2.contourId ? 1 : -1;
50183 // Segments are collinear, but belong to separate polygons
50184 return le1.isSubject ? -1 : 1;
50187 return compareEvents(le1, le2) === 1 ? 1 : -1;
50190 function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
50191 var sweepLine = new SplayTree(compareSegments);
50192 var sortedEvents = [];
50193 var rightbound = Math.min(sbbox[2], cbbox[2]);
50194 var prev, next, begin;
50196 while (eventQueue.length !== 0) {
50197 var event = eventQueue.pop();
50198 sortedEvents.push(event); // optimization by bboxes for intersection and difference goes here
50200 if (operation === INTERSECTION && event.point[0] > rightbound || operation === DIFFERENCE && event.point[0] > sbbox[2]) {
50205 next = prev = sweepLine.insert(event);
50206 begin = sweepLine.minNode();
50207 if (prev !== begin) prev = sweepLine.prev(prev);else prev = null;
50208 next = sweepLine.next(next);
50209 var prevEvent = prev ? prev.key : null;
50210 var prevprevEvent = void 0;
50211 computeFields(event, prevEvent, operation);
50214 if (possibleIntersection(event, next.key, eventQueue) === 2) {
50215 computeFields(event, prevEvent, operation);
50216 computeFields(event, next.key, operation);
50221 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
50222 var prevprev = prev;
50223 if (prevprev !== begin) prevprev = sweepLine.prev(prevprev);else prevprev = null;
50224 prevprevEvent = prevprev ? prevprev.key : null;
50225 computeFields(prevEvent, prevprevEvent, operation);
50226 computeFields(event, prevEvent, operation);
50230 event = event.otherEvent;
50231 next = prev = sweepLine.find(event);
50233 if (prev && next) {
50234 if (prev !== begin) prev = sweepLine.prev(prev);else prev = null;
50235 next = sweepLine.next(next);
50236 sweepLine.remove(event);
50238 if (next && prev) {
50239 possibleIntersection(prev.key, next.key, eventQueue);
50245 return sortedEvents;
50248 var Contour = /*#__PURE__*/function () {
50254 function Contour() {
50255 _classCallCheck(this, Contour);
50259 this.holeOf = null;
50263 _createClass(Contour, [{
50265 value: function isExterior() {
50266 return this.holeOf == null;
50274 * @param {Array.<SweepEvent>} sortedEvents
50275 * @return {Array.<SweepEvent>}
50278 function orderEvents(sortedEvents) {
50279 var event, i, len, tmp;
50280 var resultEvents = [];
50282 for (i = 0, len = sortedEvents.length; i < len; i++) {
50283 event = sortedEvents[i];
50285 if (event.left && event.inResult || !event.left && event.otherEvent.inResult) {
50286 resultEvents.push(event);
50288 } // Due to overlapping edges the resultEvents array can be not wholly sorted
50291 var sorted = false;
50296 for (i = 0, len = resultEvents.length; i < len; i++) {
50297 if (i + 1 < len && compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
50298 tmp = resultEvents[i];
50299 resultEvents[i] = resultEvents[i + 1];
50300 resultEvents[i + 1] = tmp;
50306 for (i = 0, len = resultEvents.length; i < len; i++) {
50307 event = resultEvents[i];
50308 event.otherPos = i;
50309 } // imagine, the right event is found in the beginning of the queue,
50310 // when his left counterpart is not marked yet
50313 for (i = 0, len = resultEvents.length; i < len; i++) {
50314 event = resultEvents[i];
50317 tmp = event.otherPos;
50318 event.otherPos = event.otherEvent.otherPos;
50319 event.otherEvent.otherPos = tmp;
50323 return resultEvents;
50326 * @param {Number} pos
50327 * @param {Array.<SweepEvent>} resultEvents
50328 * @param {Object>} processed
50333 function nextPos(pos, resultEvents, processed, origPos) {
50334 var newPos = pos + 1,
50335 p = resultEvents[pos].point,
50337 var length = resultEvents.length;
50338 if (newPos < length) p1 = resultEvents[newPos].point;
50340 while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
50341 if (!processed[newPos]) {
50347 p1 = resultEvents[newPos].point;
50352 while (processed[newPos] && newPos > origPos) {
50359 function initializeContourFromContext(event, contours, contourId) {
50360 var contour = new Contour();
50362 if (event.prevInResult != null) {
50363 var prevInResult = event.prevInResult; // Note that it is valid to query the "previous in result" for its output contour id,
50364 // because we must have already processed it (i.e., assigned an output contour id)
50365 // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
50368 var lowerContourId = prevInResult.outputContourId;
50369 var lowerResultTransition = prevInResult.resultTransition;
50371 if (lowerResultTransition > 0) {
50372 // We are inside. Now we have to check if the thing below us is another hole or
50373 // an exterior contour.
50374 var lowerContour = contours[lowerContourId];
50376 if (lowerContour.holeOf != null) {
50377 // The lower contour is a hole => Connect the new contour as a hole to its parent,
50378 // and use same depth.
50379 var parentContourId = lowerContour.holeOf;
50380 contours[parentContourId].holeIds.push(contourId);
50381 contour.holeOf = parentContourId;
50382 contour.depth = contours[lowerContourId].depth;
50384 // The lower contour is an exterior contour => Connect the new contour as a hole,
50385 // and increment depth.
50386 contours[lowerContourId].holeIds.push(contourId);
50387 contour.holeOf = lowerContourId;
50388 contour.depth = contours[lowerContourId].depth + 1;
50391 // We are outside => this contour is an exterior contour of same depth.
50392 contour.holeOf = null;
50393 contour.depth = contours[lowerContourId].depth;
50396 // There is no lower/previous contour => this contour is an exterior contour of depth 0.
50397 contour.holeOf = null;
50404 * @param {Array.<SweepEvent>} sortedEvents
50405 * @return {Array.<*>} polygons
50409 function connectEdges(sortedEvents) {
50411 var resultEvents = orderEvents(sortedEvents); // "false"-filled array
50413 var processed = {};
50416 var _loop = function _loop() {
50417 if (processed[i]) {
50421 var contourId = contours.length;
50422 var contour = initializeContourFromContext(resultEvents[i], contours, contourId); // Helper function that combines marking an event as processed with assigning its output contour ID
50424 var markAsProcessed = function markAsProcessed(pos) {
50425 processed[pos] = true;
50426 resultEvents[pos].outputContourId = contourId;
50431 var initial = resultEvents[i].point;
50432 contour.points.push(initial);
50433 /* eslint no-constant-condition: "off" */
50436 markAsProcessed(pos);
50437 pos = resultEvents[pos].otherPos;
50438 markAsProcessed(pos);
50439 contour.points.push(resultEvents[pos].point);
50440 pos = nextPos(pos, resultEvents, processed, origPos);
50442 if (pos == origPos) {
50447 contours.push(contour);
50450 for (i = 0, len = resultEvents.length; i < len; i++) {
50451 var _ret = _loop();
50453 if (_ret === "continue") continue;
50459 var tinyqueue = TinyQueue;
50460 var _default$1 = TinyQueue;
50462 function TinyQueue(data, compare) {
50463 if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare);
50464 this.data = data || [];
50465 this.length = this.data.length;
50466 this.compare = compare || defaultCompare$1;
50468 if (this.length > 0) {
50469 for (var i = (this.length >> 1) - 1; i >= 0; i--) {
50475 function defaultCompare$1(a, b) {
50476 return a < b ? -1 : a > b ? 1 : 0;
50479 TinyQueue.prototype = {
50480 push: function push(item) {
50481 this.data.push(item);
50484 this._up(this.length - 1);
50486 pop: function pop() {
50487 if (this.length === 0) return undefined;
50488 var top = this.data[0];
50491 if (this.length > 0) {
50492 this.data[0] = this.data[this.length];
50500 peek: function peek() {
50501 return this.data[0];
50503 _up: function _up(pos) {
50504 var data = this.data;
50505 var compare = this.compare;
50506 var item = data[pos];
50509 var parent = pos - 1 >> 1;
50510 var current = data[parent];
50511 if (compare(item, current) >= 0) break;
50512 data[pos] = current;
50518 _down: function _down(pos) {
50519 var data = this.data;
50520 var compare = this.compare;
50521 var halfLength = this.length >> 1;
50522 var item = data[pos];
50524 while (pos < halfLength) {
50525 var left = (pos << 1) + 1;
50526 var right = left + 1;
50527 var best = data[left];
50529 if (right < this.length && compare(data[right], best) < 0) {
50531 best = data[right];
50534 if (compare(best, item) >= 0) break;
50542 tinyqueue["default"] = _default$1;
50544 var max$5 = Math.max;
50545 var min$a = Math.min;
50548 function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
50549 var i, len, s1, s2, e1, e2;
50551 for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
50552 s1 = contourOrHole[i];
50553 s2 = contourOrHole[i + 1];
50554 e1 = new SweepEvent(s1, false, undefined, isSubject);
50555 e2 = new SweepEvent(s2, false, e1, isSubject);
50556 e1.otherEvent = e2;
50558 if (s1[0] === s2[0] && s1[1] === s2[1]) {
50559 continue; // skip collapsed edges, or it breaks
50562 e1.contourId = e2.contourId = depth;
50564 if (!isExteriorRing) {
50565 e1.isExteriorRing = false;
50566 e2.isExteriorRing = false;
50569 if (compareEvents(e1, e2) > 0) {
50577 bbox[0] = min$a(bbox[0], x);
50578 bbox[1] = min$a(bbox[1], y);
50579 bbox[2] = max$5(bbox[2], x);
50580 bbox[3] = max$5(bbox[3], y); // Pushing it so the queue is sorted from left to right,
50581 // with object on the left having the highest priority.
50588 function fillQueue(subject, clipping, sbbox, cbbox, operation) {
50589 var eventQueue = new tinyqueue(null, compareEvents);
50590 var polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
50592 for (i = 0, ii = subject.length; i < ii; i++) {
50593 polygonSet = subject[i];
50595 for (j = 0, jj = polygonSet.length; j < jj; j++) {
50596 isExteriorRing = j === 0;
50597 if (isExteriorRing) contourId++;
50598 processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
50602 for (i = 0, ii = clipping.length; i < ii; i++) {
50603 polygonSet = clipping[i];
50605 for (j = 0, jj = polygonSet.length; j < jj; j++) {
50606 isExteriorRing = j === 0;
50607 if (operation === DIFFERENCE) isExteriorRing = false;
50608 if (isExteriorRing) contourId++;
50609 processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
50618 function trivialOperation(subject, clipping, operation) {
50621 if (subject.length * clipping.length === 0) {
50622 if (operation === INTERSECTION) {
50624 } else if (operation === DIFFERENCE) {
50626 } else if (operation === UNION || operation === XOR) {
50627 result = subject.length === 0 ? clipping : subject;
50634 function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
50637 if (sbbox[0] > cbbox[2] || cbbox[0] > sbbox[2] || sbbox[1] > cbbox[3] || cbbox[1] > sbbox[3]) {
50638 if (operation === INTERSECTION) {
50640 } else if (operation === DIFFERENCE) {
50642 } else if (operation === UNION || operation === XOR) {
50643 result = subject.concat(clipping);
50650 function _boolean(subject, clipping, operation) {
50651 if (typeof subject[0][0][0] === 'number') {
50652 subject = [subject];
50655 if (typeof clipping[0][0][0] === 'number') {
50656 clipping = [clipping];
50659 var trivial = trivialOperation(subject, clipping, operation);
50662 return trivial === EMPTY ? null : trivial;
50665 var sbbox = [Infinity, Infinity, -Infinity, -Infinity];
50666 var cbbox = [Infinity, Infinity, -Infinity, -Infinity]; // console.time('fill queue');
50668 var eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation); //console.timeEnd('fill queue');
50670 trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
50673 return trivial === EMPTY ? null : trivial;
50674 } // console.time('subdivide edges');
50677 var sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation); //console.timeEnd('subdivide edges');
50678 // console.time('connect vertices');
50680 var contours = connectEdges(sortedEvents); //console.timeEnd('connect vertices');
50681 // Convert contours to polygons
50685 for (var i = 0; i < contours.length; i++) {
50686 var contour = contours[i];
50688 if (contour.isExterior()) {
50689 // The exterior ring goes first
50690 var rings = [contour.points]; // Followed by holes if any
50692 for (var j = 0; j < contour.holeIds.length; j++) {
50693 var holeId = contour.holeIds[j];
50694 rings.push(contours[holeId].points);
50697 polygons.push(rings);
50704 function union(subject, clipping) {
50705 return _boolean(subject, clipping, UNION);
50708 /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
50709 var read$6 = function read(buffer, offset, isLE, mLen, nBytes) {
50711 var eLen = nBytes * 8 - mLen - 1;
50712 var eMax = (1 << eLen) - 1;
50713 var eBias = eMax >> 1;
50715 var i = isLE ? nBytes - 1 : 0;
50716 var d = isLE ? -1 : 1;
50717 var s = buffer[offset + i];
50719 e = s & (1 << -nBits) - 1;
50723 for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
50725 m = e & (1 << -nBits) - 1;
50729 for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
50733 } else if (e === eMax) {
50734 return m ? NaN : (s ? -1 : 1) * Infinity;
50736 m = m + Math.pow(2, mLen);
50740 return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
50743 var write$6 = function write(buffer, value, offset, isLE, mLen, nBytes) {
50745 var eLen = nBytes * 8 - mLen - 1;
50746 var eMax = (1 << eLen) - 1;
50747 var eBias = eMax >> 1;
50748 var rt = mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0;
50749 var i = isLE ? 0 : nBytes - 1;
50750 var d = isLE ? 1 : -1;
50751 var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
50752 value = Math.abs(value);
50754 if (isNaN(value) || value === Infinity) {
50755 m = isNaN(value) ? 1 : 0;
50758 e = Math.floor(Math.log(value) / Math.LN2);
50760 if (value * (c = Math.pow(2, -e)) < 1) {
50765 if (e + eBias >= 1) {
50768 value += rt * Math.pow(2, 1 - eBias);
50771 if (value * c >= 2) {
50776 if (e + eBias >= eMax) {
50779 } else if (e + eBias >= 1) {
50780 m = (value * c - 1) * Math.pow(2, mLen);
50783 m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
50788 for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
50793 for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
50795 buffer[offset + i - d] |= s * 128;
50805 function Pbf(buf) {
50806 this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
50809 this.length = this.buf.length;
50812 Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
50814 Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
50816 Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
50818 Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
50820 var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
50821 SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32; // Threshold chosen based on both benchmarking and knowledge about browser string
50822 // data structures (which currently switch structure types at 12 bytes or more)
50824 var TEXT_DECODER_MIN_LENGTH = 12;
50825 var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
50827 destroy: function destroy() {
50830 // === READING =================================================================
50831 readFields: function readFields(readField, result, end) {
50832 end = end || this.length;
50834 while (this.pos < end) {
50835 var val = this.readVarint(),
50837 startPos = this.pos;
50838 this.type = val & 0x7;
50839 readField(tag, result, this);
50840 if (this.pos === startPos) this.skip(val);
50845 readMessage: function readMessage(readField, result) {
50846 return this.readFields(readField, result, this.readVarint() + this.pos);
50848 readFixed32: function readFixed32() {
50849 var val = readUInt32(this.buf, this.pos);
50853 readSFixed32: function readSFixed32() {
50854 var val = readInt32(this.buf, this.pos);
50858 // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
50859 readFixed64: function readFixed64() {
50860 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50864 readSFixed64: function readSFixed64() {
50865 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
50869 readFloat: function readFloat() {
50870 var val = ieee754$1.read(this.buf, this.pos, true, 23, 4);
50874 readDouble: function readDouble() {
50875 var val = ieee754$1.read(this.buf, this.pos, true, 52, 8);
50879 readVarint: function readVarint(isSigned) {
50880 var buf = this.buf,
50883 b = buf[this.pos++];
50885 if (b < 0x80) return val;
50886 b = buf[this.pos++];
50887 val |= (b & 0x7f) << 7;
50888 if (b < 0x80) return val;
50889 b = buf[this.pos++];
50890 val |= (b & 0x7f) << 14;
50891 if (b < 0x80) return val;
50892 b = buf[this.pos++];
50893 val |= (b & 0x7f) << 21;
50894 if (b < 0x80) return val;
50896 val |= (b & 0x0f) << 28;
50897 return readVarintRemainder(val, isSigned, this);
50899 readVarint64: function readVarint64() {
50900 // for compatibility with v2.0.1
50901 return this.readVarint(true);
50903 readSVarint: function readSVarint() {
50904 var num = this.readVarint();
50905 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
50907 readBoolean: function readBoolean() {
50908 return Boolean(this.readVarint());
50910 readString: function readString() {
50911 var end = this.readVarint() + this.pos;
50912 var pos = this.pos;
50915 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
50916 // longer strings are fast with the built-in browser TextDecoder API
50917 return readUtf8TextDecoder(this.buf, pos, end);
50918 } // short strings are fast with our custom implementation
50921 return readUtf8(this.buf, pos, end);
50923 readBytes: function readBytes() {
50924 var end = this.readVarint() + this.pos,
50925 buffer = this.buf.subarray(this.pos, end);
50929 // verbose for performance reasons; doesn't affect gzipped size
50930 readPackedVarint: function readPackedVarint(arr, isSigned) {
50931 if (this.type !== Pbf.Bytes) return arr.push(this.readVarint(isSigned));
50932 var end = readPackedEnd(this);
50935 while (this.pos < end) {
50936 arr.push(this.readVarint(isSigned));
50941 readPackedSVarint: function readPackedSVarint(arr) {
50942 if (this.type !== Pbf.Bytes) return arr.push(this.readSVarint());
50943 var end = readPackedEnd(this);
50946 while (this.pos < end) {
50947 arr.push(this.readSVarint());
50952 readPackedBoolean: function readPackedBoolean(arr) {
50953 if (this.type !== Pbf.Bytes) return arr.push(this.readBoolean());
50954 var end = readPackedEnd(this);
50957 while (this.pos < end) {
50958 arr.push(this.readBoolean());
50963 readPackedFloat: function readPackedFloat(arr) {
50964 if (this.type !== Pbf.Bytes) return arr.push(this.readFloat());
50965 var end = readPackedEnd(this);
50968 while (this.pos < end) {
50969 arr.push(this.readFloat());
50974 readPackedDouble: function readPackedDouble(arr) {
50975 if (this.type !== Pbf.Bytes) return arr.push(this.readDouble());
50976 var end = readPackedEnd(this);
50979 while (this.pos < end) {
50980 arr.push(this.readDouble());
50985 readPackedFixed32: function readPackedFixed32(arr) {
50986 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed32());
50987 var end = readPackedEnd(this);
50990 while (this.pos < end) {
50991 arr.push(this.readFixed32());
50996 readPackedSFixed32: function readPackedSFixed32(arr) {
50997 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed32());
50998 var end = readPackedEnd(this);
51001 while (this.pos < end) {
51002 arr.push(this.readSFixed32());
51007 readPackedFixed64: function readPackedFixed64(arr) {
51008 if (this.type !== Pbf.Bytes) return arr.push(this.readFixed64());
51009 var end = readPackedEnd(this);
51012 while (this.pos < end) {
51013 arr.push(this.readFixed64());
51018 readPackedSFixed64: function readPackedSFixed64(arr) {
51019 if (this.type !== Pbf.Bytes) return arr.push(this.readSFixed64());
51020 var end = readPackedEnd(this);
51023 while (this.pos < end) {
51024 arr.push(this.readSFixed64());
51029 skip: function skip(val) {
51030 var type = val & 0x7;
51031 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);
51033 // === WRITING =================================================================
51034 writeTag: function writeTag(tag, type) {
51035 this.writeVarint(tag << 3 | type);
51037 realloc: function realloc(min) {
51038 var length = this.length || 16;
51040 while (length < this.pos + min) {
51044 if (length !== this.length) {
51045 var buf = new Uint8Array(length);
51048 this.length = length;
51051 finish: function finish() {
51052 this.length = this.pos;
51054 return this.buf.subarray(0, this.length);
51056 writeFixed32: function writeFixed32(val) {
51058 writeInt32(this.buf, val, this.pos);
51061 writeSFixed32: function writeSFixed32(val) {
51063 writeInt32(this.buf, val, this.pos);
51066 writeFixed64: function writeFixed64(val) {
51068 writeInt32(this.buf, val & -1, this.pos);
51069 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
51072 writeSFixed64: function writeSFixed64(val) {
51074 writeInt32(this.buf, val & -1, this.pos);
51075 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
51078 writeVarint: function writeVarint(val) {
51081 if (val > 0xfffffff || val < 0) {
51082 writeBigVarint(val, this);
51087 this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0);
51088 if (val <= 0x7f) return;
51089 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
51090 if (val <= 0x7f) return;
51091 this.buf[this.pos++] = (val >>>= 7) & 0x7f | (val > 0x7f ? 0x80 : 0);
51092 if (val <= 0x7f) return;
51093 this.buf[this.pos++] = val >>> 7 & 0x7f;
51095 writeSVarint: function writeSVarint(val) {
51096 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
51098 writeBoolean: function writeBoolean(val) {
51099 this.writeVarint(Boolean(val));
51101 writeString: function writeString(str) {
51103 this.realloc(str.length * 4);
51104 this.pos++; // reserve 1 byte for short string length
51106 var startPos = this.pos; // write the string directly to the buffer and see how much was written
51108 this.pos = writeUtf8(this.buf, str, this.pos);
51109 var len = this.pos - startPos;
51110 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
51112 this.pos = startPos - 1;
51113 this.writeVarint(len);
51116 writeFloat: function writeFloat(val) {
51118 ieee754$1.write(this.buf, val, this.pos, true, 23, 4);
51121 writeDouble: function writeDouble(val) {
51123 ieee754$1.write(this.buf, val, this.pos, true, 52, 8);
51126 writeBytes: function writeBytes(buffer) {
51127 var len = buffer.length;
51128 this.writeVarint(len);
51131 for (var i = 0; i < len; i++) {
51132 this.buf[this.pos++] = buffer[i];
51135 writeRawMessage: function writeRawMessage(fn, obj) {
51136 this.pos++; // reserve 1 byte for short message length
51137 // write the message directly to the buffer and see how much was written
51139 var startPos = this.pos;
51141 var len = this.pos - startPos;
51142 if (len >= 0x80) makeRoomForExtraLength(startPos, len, this); // finally, write the message length in the reserved place and restore the position
51144 this.pos = startPos - 1;
51145 this.writeVarint(len);
51148 writeMessage: function writeMessage(tag, fn, obj) {
51149 this.writeTag(tag, Pbf.Bytes);
51150 this.writeRawMessage(fn, obj);
51152 writePackedVarint: function writePackedVarint(tag, arr) {
51153 if (arr.length) this.writeMessage(tag, _writePackedVarint, arr);
51155 writePackedSVarint: function writePackedSVarint(tag, arr) {
51156 if (arr.length) this.writeMessage(tag, _writePackedSVarint, arr);
51158 writePackedBoolean: function writePackedBoolean(tag, arr) {
51159 if (arr.length) this.writeMessage(tag, _writePackedBoolean, arr);
51161 writePackedFloat: function writePackedFloat(tag, arr) {
51162 if (arr.length) this.writeMessage(tag, _writePackedFloat, arr);
51164 writePackedDouble: function writePackedDouble(tag, arr) {
51165 if (arr.length) this.writeMessage(tag, _writePackedDouble, arr);
51167 writePackedFixed32: function writePackedFixed32(tag, arr) {
51168 if (arr.length) this.writeMessage(tag, _writePackedFixed, arr);
51170 writePackedSFixed32: function writePackedSFixed32(tag, arr) {
51171 if (arr.length) this.writeMessage(tag, _writePackedSFixed, arr);
51173 writePackedFixed64: function writePackedFixed64(tag, arr) {
51174 if (arr.length) this.writeMessage(tag, _writePackedFixed2, arr);
51176 writePackedSFixed64: function writePackedSFixed64(tag, arr) {
51177 if (arr.length) this.writeMessage(tag, _writePackedSFixed2, arr);
51179 writeBytesField: function writeBytesField(tag, buffer) {
51180 this.writeTag(tag, Pbf.Bytes);
51181 this.writeBytes(buffer);
51183 writeFixed32Field: function writeFixed32Field(tag, val) {
51184 this.writeTag(tag, Pbf.Fixed32);
51185 this.writeFixed32(val);
51187 writeSFixed32Field: function writeSFixed32Field(tag, val) {
51188 this.writeTag(tag, Pbf.Fixed32);
51189 this.writeSFixed32(val);
51191 writeFixed64Field: function writeFixed64Field(tag, val) {
51192 this.writeTag(tag, Pbf.Fixed64);
51193 this.writeFixed64(val);
51195 writeSFixed64Field: function writeSFixed64Field(tag, val) {
51196 this.writeTag(tag, Pbf.Fixed64);
51197 this.writeSFixed64(val);
51199 writeVarintField: function writeVarintField(tag, val) {
51200 this.writeTag(tag, Pbf.Varint);
51201 this.writeVarint(val);
51203 writeSVarintField: function writeSVarintField(tag, val) {
51204 this.writeTag(tag, Pbf.Varint);
51205 this.writeSVarint(val);
51207 writeStringField: function writeStringField(tag, str) {
51208 this.writeTag(tag, Pbf.Bytes);
51209 this.writeString(str);
51211 writeFloatField: function writeFloatField(tag, val) {
51212 this.writeTag(tag, Pbf.Fixed32);
51213 this.writeFloat(val);
51215 writeDoubleField: function writeDoubleField(tag, val) {
51216 this.writeTag(tag, Pbf.Fixed64);
51217 this.writeDouble(val);
51219 writeBooleanField: function writeBooleanField(tag, val) {
51220 this.writeVarintField(tag, Boolean(val));
51224 function readVarintRemainder(l, s, p) {
51229 h = (b & 0x70) >> 4;
51230 if (b < 0x80) return toNum(l, h, s);
51232 h |= (b & 0x7f) << 3;
51233 if (b < 0x80) return toNum(l, h, s);
51235 h |= (b & 0x7f) << 10;
51236 if (b < 0x80) return toNum(l, h, s);
51238 h |= (b & 0x7f) << 17;
51239 if (b < 0x80) return toNum(l, h, s);
51241 h |= (b & 0x7f) << 24;
51242 if (b < 0x80) return toNum(l, h, s);
51244 h |= (b & 0x01) << 31;
51245 if (b < 0x80) return toNum(l, h, s);
51246 throw new Error('Expected varint not more than 10 bytes');
51249 function readPackedEnd(pbf) {
51250 return pbf.type === Pbf.Bytes ? pbf.readVarint() + pbf.pos : pbf.pos + 1;
51253 function toNum(low, high, isSigned) {
51255 return high * 0x100000000 + (low >>> 0);
51258 return (high >>> 0) * 0x100000000 + (low >>> 0);
51261 function writeBigVarint(val, pbf) {
51265 low = val % 0x100000000 | 0;
51266 high = val / 0x100000000 | 0;
51268 low = ~(-val % 0x100000000);
51269 high = ~(-val / 0x100000000);
51271 if (low ^ 0xffffffff) {
51275 high = high + 1 | 0;
51279 if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
51280 throw new Error('Given varint doesn\'t fit into 10 bytes');
51284 writeBigVarintLow(low, high, pbf);
51285 writeBigVarintHigh(high, pbf);
51288 function writeBigVarintLow(low, high, pbf) {
51289 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51291 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51293 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51295 pbf.buf[pbf.pos++] = low & 0x7f | 0x80;
51297 pbf.buf[pbf.pos] = low & 0x7f;
51300 function writeBigVarintHigh(high, pbf) {
51301 var lsb = (high & 0x07) << 4;
51302 pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0);
51304 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51306 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51308 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51310 pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0);
51312 pbf.buf[pbf.pos++] = high & 0x7f;
51315 function makeRoomForExtraLength(startPos, len, pbf) {
51316 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
51318 pbf.realloc(extraLen);
51320 for (var i = pbf.pos - 1; i >= startPos; i--) {
51321 pbf.buf[i + extraLen] = pbf.buf[i];
51325 function _writePackedVarint(arr, pbf) {
51326 for (var i = 0; i < arr.length; i++) {
51327 pbf.writeVarint(arr[i]);
51331 function _writePackedSVarint(arr, pbf) {
51332 for (var i = 0; i < arr.length; i++) {
51333 pbf.writeSVarint(arr[i]);
51337 function _writePackedFloat(arr, pbf) {
51338 for (var i = 0; i < arr.length; i++) {
51339 pbf.writeFloat(arr[i]);
51343 function _writePackedDouble(arr, pbf) {
51344 for (var i = 0; i < arr.length; i++) {
51345 pbf.writeDouble(arr[i]);
51349 function _writePackedBoolean(arr, pbf) {
51350 for (var i = 0; i < arr.length; i++) {
51351 pbf.writeBoolean(arr[i]);
51355 function _writePackedFixed(arr, pbf) {
51356 for (var i = 0; i < arr.length; i++) {
51357 pbf.writeFixed32(arr[i]);
51361 function _writePackedSFixed(arr, pbf) {
51362 for (var i = 0; i < arr.length; i++) {
51363 pbf.writeSFixed32(arr[i]);
51367 function _writePackedFixed2(arr, pbf) {
51368 for (var i = 0; i < arr.length; i++) {
51369 pbf.writeFixed64(arr[i]);
51373 function _writePackedSFixed2(arr, pbf) {
51374 for (var i = 0; i < arr.length; i++) {
51375 pbf.writeSFixed64(arr[i]);
51377 } // Buffer code below from https://github.com/feross/buffer, MIT-licensed
51380 function readUInt32(buf, pos) {
51381 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + buf[pos + 3] * 0x1000000;
51384 function writeInt32(buf, val, pos) {
51386 buf[pos + 1] = val >>> 8;
51387 buf[pos + 2] = val >>> 16;
51388 buf[pos + 3] = val >>> 24;
51391 function readInt32(buf, pos) {
51392 return (buf[pos] | buf[pos + 1] << 8 | buf[pos + 2] << 16) + (buf[pos + 3] << 24);
51395 function readUtf8(buf, pos, end) {
51401 var c = null; // codepoint
51403 var bytesPerSequence = b0 > 0xEF ? 4 : b0 > 0xDF ? 3 : b0 > 0xBF ? 2 : 1;
51404 if (i + bytesPerSequence > end) break;
51407 if (bytesPerSequence === 1) {
51411 } else if (bytesPerSequence === 2) {
51414 if ((b1 & 0xC0) === 0x80) {
51415 c = (b0 & 0x1F) << 0x6 | b1 & 0x3F;
51421 } else if (bytesPerSequence === 3) {
51425 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
51426 c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | b2 & 0x3F;
51428 if (c <= 0x7FF || c >= 0xD800 && c <= 0xDFFF) {
51432 } else if (bytesPerSequence === 4) {
51437 if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
51438 c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | b3 & 0x3F;
51440 if (c <= 0xFFFF || c >= 0x110000) {
51448 bytesPerSequence = 1;
51449 } else if (c > 0xFFFF) {
51451 str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
51452 c = 0xDC00 | c & 0x3FF;
51455 str += String.fromCharCode(c);
51456 i += bytesPerSequence;
51462 function readUtf8TextDecoder(buf, pos, end) {
51463 return utf8TextDecoder.decode(buf.subarray(pos, end));
51466 function writeUtf8(buf, str, pos) {
51467 for (var i = 0, c, lead; i < str.length; i++) {
51468 c = str.charCodeAt(i); // code point
51470 if (c > 0xD7FF && c < 0xE000) {
51479 c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
51483 if (c > 0xDBFF || i + 1 === str.length) {
51504 buf[pos++] = c >> 0x6 | 0xC0;
51507 buf[pos++] = c >> 0xC | 0xE0;
51509 buf[pos++] = c >> 0x12 | 0xF0;
51510 buf[pos++] = c >> 0xC & 0x3F | 0x80;
51513 buf[pos++] = c >> 0x6 & 0x3F | 0x80;
51516 buf[pos++] = c & 0x3F | 0x80;
51523 var pointGeometry = Point;
51525 * A standalone point geometry with useful accessor, comparison, and
51526 * modification methods.
51529 * @param {Number} x the x-coordinate. this could be longitude or screen
51530 * pixels, or any other sort of unit.
51531 * @param {Number} y the y-coordinate. this could be latitude or screen
51532 * pixels, or any other sort of unit.
51534 * var point = new Point(-77, 38);
51537 function Point(x, y) {
51542 Point.prototype = {
51544 * Clone this point, returning a new point that can be modified
51545 * without affecting the old one.
51546 * @return {Point} the clone
51548 clone: function clone() {
51549 return new Point(this.x, this.y);
51553 * Add this point's x & y coordinates to another point,
51554 * yielding a new point.
51555 * @param {Point} p the other point
51556 * @return {Point} output point
51558 add: function add(p) {
51559 return this.clone()._add(p);
51563 * Subtract this point's x & y coordinates to from point,
51564 * yielding a new point.
51565 * @param {Point} p the other point
51566 * @return {Point} output point
51568 sub: function sub(p) {
51569 return this.clone()._sub(p);
51573 * Multiply this point's x & y coordinates by point,
51574 * yielding a new point.
51575 * @param {Point} p the other point
51576 * @return {Point} output point
51578 multByPoint: function multByPoint(p) {
51579 return this.clone()._multByPoint(p);
51583 * Divide this point's x & y coordinates by point,
51584 * yielding a new point.
51585 * @param {Point} p the other point
51586 * @return {Point} output point
51588 divByPoint: function divByPoint(p) {
51589 return this.clone()._divByPoint(p);
51593 * Multiply this point's x & y coordinates by a factor,
51594 * yielding a new point.
51595 * @param {Point} k factor
51596 * @return {Point} output point
51598 mult: function mult(k) {
51599 return this.clone()._mult(k);
51603 * Divide this point's x & y coordinates by a factor,
51604 * yielding a new point.
51605 * @param {Point} k factor
51606 * @return {Point} output point
51608 div: function div(k) {
51609 return this.clone()._div(k);
51613 * Rotate this point around the 0, 0 origin by an angle a,
51615 * @param {Number} a angle to rotate around, in radians
51616 * @return {Point} output point
51618 rotate: function rotate(a) {
51619 return this.clone()._rotate(a);
51623 * Rotate this point around p point by an angle a,
51625 * @param {Number} a angle to rotate around, in radians
51626 * @param {Point} p Point to rotate around
51627 * @return {Point} output point
51629 rotateAround: function rotateAround(a, p) {
51630 return this.clone()._rotateAround(a, p);
51634 * Multiply this point by a 4x1 transformation matrix
51635 * @param {Array<Number>} m transformation matrix
51636 * @return {Point} output point
51638 matMult: function matMult(m) {
51639 return this.clone()._matMult(m);
51643 * Calculate this point but as a unit vector from 0, 0, meaning
51644 * that the distance from the resulting point to the 0, 0
51645 * coordinate will be equal to 1 and the angle from the resulting
51646 * point to the 0, 0 coordinate will be the same as before.
51647 * @return {Point} unit vector point
51649 unit: function unit() {
51650 return this.clone()._unit();
51654 * Compute a perpendicular point, where the new y coordinate
51655 * is the old x coordinate and the new x coordinate is the old y
51656 * coordinate multiplied by -1
51657 * @return {Point} perpendicular point
51659 perp: function perp() {
51660 return this.clone()._perp();
51664 * Return a version of this point with the x & y coordinates
51665 * rounded to integers.
51666 * @return {Point} rounded point
51668 round: function round() {
51669 return this.clone()._round();
51673 * Return the magitude of this point: this is the Euclidean
51674 * distance from the 0, 0 coordinate to this point's x and y
51676 * @return {Number} magnitude
51678 mag: function mag() {
51679 return Math.sqrt(this.x * this.x + this.y * this.y);
51683 * Judge whether this point is equal to another point, returning
51685 * @param {Point} other the other point
51686 * @return {boolean} whether the points are equal
51688 equals: function equals(other) {
51689 return this.x === other.x && this.y === other.y;
51693 * Calculate the distance from this point to another point
51694 * @param {Point} p the other point
51695 * @return {Number} distance
51697 dist: function dist(p) {
51698 return Math.sqrt(this.distSqr(p));
51702 * Calculate the distance from this point to another point,
51703 * without the square root step. Useful if you're comparing
51704 * relative distances.
51705 * @param {Point} p the other point
51706 * @return {Number} distance
51708 distSqr: function distSqr(p) {
51709 var dx = p.x - this.x,
51711 return dx * dx + dy * dy;
51715 * Get the angle from the 0, 0 coordinate to this point, in radians
51717 * @return {Number} angle
51719 angle: function angle() {
51720 return Math.atan2(this.y, this.x);
51724 * Get the angle from this point to another point, in radians
51725 * @param {Point} b the other point
51726 * @return {Number} angle
51728 angleTo: function angleTo(b) {
51729 return Math.atan2(this.y - b.y, this.x - b.x);
51733 * Get the angle between this point and another point, in radians
51734 * @param {Point} b the other point
51735 * @return {Number} angle
51737 angleWith: function angleWith(b) {
51738 return this.angleWithSep(b.x, b.y);
51742 * Find the angle of the two vectors, solving the formula for
51743 * the cross product a x b = |a||b|sin(θ) for θ.
51744 * @param {Number} x the x-coordinate
51745 * @param {Number} y the y-coordinate
51746 * @return {Number} the angle in radians
51748 angleWithSep: function angleWithSep(x, y) {
51749 return Math.atan2(this.x * y - this.y * x, this.x * x + this.y * y);
51751 _matMult: function _matMult(m) {
51752 var x = m[0] * this.x + m[1] * this.y,
51753 y = m[2] * this.x + m[3] * this.y;
51758 _add: function _add(p) {
51763 _sub: function _sub(p) {
51768 _mult: function _mult(k) {
51773 _div: function _div(k) {
51778 _multByPoint: function _multByPoint(p) {
51783 _divByPoint: function _divByPoint(p) {
51788 _unit: function _unit() {
51789 this._div(this.mag());
51793 _perp: function _perp() {
51799 _rotate: function _rotate(angle) {
51800 var cos = Math.cos(angle),
51801 sin = Math.sin(angle),
51802 x = cos * this.x - sin * this.y,
51803 y = sin * this.x + cos * this.y;
51808 _rotateAround: function _rotateAround(angle, p) {
51809 var cos = Math.cos(angle),
51810 sin = Math.sin(angle),
51811 x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
51812 y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
51817 _round: function _round() {
51818 this.x = Math.round(this.x);
51819 this.y = Math.round(this.y);
51824 * Construct a point from an array if necessary, otherwise if the input
51825 * is already a Point, or an unknown type, return it unchanged
51826 * @param {Array<Number>|Point|*} a any kind of input value
51827 * @return {Point} constructed point, or passed-through value.
51830 * var point = Point.convert([0, 1]);
51831 * // is equivalent to
51832 * var point = new Point(0, 1);
51835 Point.convert = function (a) {
51836 if (a instanceof Point) {
51840 if (Array.isArray(a)) {
51841 return new Point(a[0], a[1]);
51847 var vectortilefeature = VectorTileFeature;
51849 function VectorTileFeature(pbf, end, extent, keys, values) {
51851 this.properties = {};
51852 this.extent = extent;
51853 this.type = 0; // Private
51856 this._geometry = -1;
51858 this._values = values;
51859 pbf.readFields(readFeature, this, end);
51862 function readFeature(tag, feature, pbf) {
51863 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;
51866 function readTag(pbf, feature) {
51867 var end = pbf.readVarint() + pbf.pos;
51869 while (pbf.pos < end) {
51870 var key = feature._keys[pbf.readVarint()],
51871 value = feature._values[pbf.readVarint()];
51873 feature.properties[key] = value;
51877 VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
51879 VectorTileFeature.prototype.loadGeometry = function () {
51880 var pbf = this._pbf;
51881 pbf.pos = this._geometry;
51882 var end = pbf.readVarint() + pbf.pos,
51890 while (pbf.pos < end) {
51892 var cmdLen = pbf.readVarint();
51893 cmd = cmdLen & 0x7;
51894 length = cmdLen >> 3;
51899 if (cmd === 1 || cmd === 2) {
51900 x += pbf.readSVarint();
51901 y += pbf.readSVarint();
51905 if (line) lines.push(line);
51909 line.push(new pointGeometry(x, y));
51910 } else if (cmd === 7) {
51911 // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
51913 line.push(line[0].clone()); // closePolygon
51916 throw new Error('unknown command ' + cmd);
51920 if (line) lines.push(line);
51924 VectorTileFeature.prototype.bbox = function () {
51925 var pbf = this._pbf;
51926 pbf.pos = this._geometry;
51927 var end = pbf.readVarint() + pbf.pos,
51937 while (pbf.pos < end) {
51939 var cmdLen = pbf.readVarint();
51940 cmd = cmdLen & 0x7;
51941 length = cmdLen >> 3;
51946 if (cmd === 1 || cmd === 2) {
51947 x += pbf.readSVarint();
51948 y += pbf.readSVarint();
51949 if (x < x1) x1 = x;
51950 if (x > x2) x2 = x;
51951 if (y < y1) y1 = y;
51952 if (y > y2) y2 = y;
51953 } else if (cmd !== 7) {
51954 throw new Error('unknown command ' + cmd);
51958 return [x1, y1, x2, y2];
51961 VectorTileFeature.prototype.toGeoJSON = function (x, y, z) {
51962 var size = this.extent * Math.pow(2, z),
51963 x0 = this.extent * x,
51964 y0 = this.extent * y,
51965 coords = this.loadGeometry(),
51966 type = VectorTileFeature.types[this.type],
51970 function project(line) {
51971 for (var j = 0; j < line.length; j++) {
51973 y2 = 180 - (p.y + y0) * 360 / size;
51974 line[j] = [(p.x + x0) * 360 / size - 180, 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90];
51978 switch (this.type) {
51982 for (i = 0; i < coords.length; i++) {
51983 points[i] = coords[i][0];
51991 for (i = 0; i < coords.length; i++) {
51992 project(coords[i]);
51998 coords = classifyRings(coords);
52000 for (i = 0; i < coords.length; i++) {
52001 for (j = 0; j < coords[i].length; j++) {
52002 project(coords[i][j]);
52009 if (coords.length === 1) {
52010 coords = coords[0];
52012 type = 'Multi' + type;
52019 coordinates: coords
52021 properties: this.properties
52024 if ('id' in this) {
52025 result.id = this.id;
52029 }; // classifies an array of rings into polygons with outer rings and holes
52032 function classifyRings(rings) {
52033 var len = rings.length;
52034 if (len <= 1) return [rings];
52039 for (var i = 0; i < len; i++) {
52040 var area = signedArea$1(rings[i]);
52041 if (area === 0) continue;
52042 if (ccw === undefined) ccw = area < 0;
52044 if (ccw === area < 0) {
52045 if (polygon) polygons.push(polygon);
52046 polygon = [rings[i]];
52048 polygon.push(rings[i]);
52052 if (polygon) polygons.push(polygon);
52056 function signedArea$1(ring) {
52059 for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
52062 sum += (p2.x - p1.x) * (p1.y + p2.y);
52068 var vectortilelayer = VectorTileLayer;
52070 function VectorTileLayer(pbf, end) {
52074 this.extent = 4096;
52075 this.length = 0; // Private
52080 this._features = [];
52081 pbf.readFields(readLayer, this, end);
52082 this.length = this._features.length;
52085 function readLayer(tag, layer, pbf) {
52086 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));
52089 function readValueMessage(pbf) {
52091 end = pbf.readVarint() + pbf.pos;
52093 while (pbf.pos < end) {
52094 var tag = pbf.readVarint() >> 3;
52095 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;
52099 } // return feature `i` from this layer as a `VectorTileFeature`
52102 VectorTileLayer.prototype.feature = function (i) {
52103 if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds');
52104 this._pbf.pos = this._features[i];
52106 var end = this._pbf.readVarint() + this._pbf.pos;
52108 return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
52111 var vectortile = VectorTile;
52113 function VectorTile(pbf, end) {
52114 this.layers = pbf.readFields(readTile, {}, end);
52117 function readTile(tag, layers, pbf) {
52119 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
52120 if (layer.length) layers[layer.name] = layer;
52124 var VectorTile$1 = vectortile;
52125 var VectorTileFeature$1 = vectortilefeature;
52126 var VectorTileLayer$1 = vectortilelayer;
52128 VectorTile: VectorTile$1,
52129 VectorTileFeature: VectorTileFeature$1,
52130 VectorTileLayer: VectorTileLayer$1
52133 var tiler$7 = utilTiler().tileSize(512).margin(1);
52134 var dispatch$8 = dispatch('loadedData');
52138 function abortRequest$7(controller) {
52139 controller.abort();
52142 function vtToGeoJSON(data, tile, mergeCache) {
52143 var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
52144 var layers = Object.keys(vectorTile$1.layers);
52146 if (!Array.isArray(layers)) {
52151 layers.forEach(function (layerID) {
52152 var layer = vectorTile$1.layers[layerID];
52155 for (var i = 0; i < layer.length; i++) {
52156 var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
52157 var geometry = feature.geometry; // Treat all Polygons as MultiPolygons
52159 if (geometry.type === 'Polygon') {
52160 geometry.type = 'MultiPolygon';
52161 geometry.coordinates = [geometry.coordinates];
52164 var isClipped = false; // Clip to tile bounds
52166 if (geometry.type === 'MultiPolygon') {
52167 var featureClip = turf_bboxClip(feature, tile.extent.rectangle());
52169 if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
52170 // feature = featureClip;
52174 if (!feature.geometry.coordinates.length) continue; // not actually on this tile
52176 if (!feature.geometry.coordinates[0].length) continue; // not actually on this tile
52177 } // Generate some unique IDs and add some metadata
52180 var featurehash = utilHashcode(fastJsonStableStringify(feature));
52181 var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
52182 feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
52183 feature.__featurehash__ = featurehash;
52184 feature.__propertyhash__ = propertyhash;
52185 features.push(feature); // Clipped Polygons at same zoom with identical properties can get merged
52187 if (isClipped && geometry.type === 'MultiPolygon') {
52188 var merged = mergeCache[propertyhash];
52190 if (merged && merged.length) {
52191 var other = merged[0];
52192 var coords = union(feature.geometry.coordinates, other.geometry.coordinates);
52194 if (!coords || !coords.length) {
52195 continue; // something failed in martinez union
52198 merged.push(feature);
52200 for (var j = 0; j < merged.length; j++) {
52201 // all these features get...
52202 merged[j].geometry.coordinates = coords; // same coords
52204 merged[j].__featurehash__ = featurehash; // same hash, so deduplication works
52207 mergeCache[propertyhash] = [feature];
52216 function loadTile(source, tile) {
52217 if (source.loaded[tile.id] || source.inflight[tile.id]) return;
52218 var url = source.template.replace('{x}', tile.xyz[0]).replace('{y}', tile.xyz[1]) // TMS-flipped y coordinate
52219 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1).replace(/\{z(oom)?\}/, tile.xyz[2]).replace(/\{switch:([^}]+)\}/, function (s, r) {
52220 var subdomains = r.split(',');
52221 return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
52223 var controller = new AbortController();
52224 source.inflight[tile.id] = controller;
52226 signal: controller.signal
52227 }).then(function (response) {
52228 if (!response.ok) {
52229 throw new Error(response.status + ' ' + response.statusText);
52232 source.loaded[tile.id] = [];
52233 delete source.inflight[tile.id];
52234 return response.arrayBuffer();
52235 }).then(function (data) {
52237 throw new Error('No Data');
52240 var z = tile.xyz[2];
52242 if (!source.canMerge[z]) {
52243 source.canMerge[z] = {}; // initialize mergeCache
52246 source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
52247 dispatch$8.call('loadedData');
52248 })["catch"](function () {
52249 source.loaded[tile.id] = [];
52250 delete source.inflight[tile.id];
52254 var serviceVectorTile = {
52255 init: function init() {
52260 this.event = utilRebind(this, dispatch$8, 'on');
52262 reset: function reset() {
52263 for (var sourceID in _vtCache) {
52264 var source = _vtCache[sourceID];
52266 if (source && source.inflight) {
52267 Object.values(source.inflight).forEach(abortRequest$7);
52273 addSource: function addSource(sourceID, template) {
52274 _vtCache[sourceID] = {
52275 template: template,
52280 return _vtCache[sourceID];
52282 data: function data(sourceID, projection) {
52283 var source = _vtCache[sourceID];
52284 if (!source) return [];
52285 var tiles = tiler$7.getTiles(projection);
52289 for (var i = 0; i < tiles.length; i++) {
52290 var features = source.loaded[tiles[i].id];
52291 if (!features || !features.length) continue;
52293 for (var j = 0; j < features.length; j++) {
52294 var feature = features[j];
52295 var hash = feature.__featurehash__;
52296 if (seen[hash]) continue;
52297 seen[hash] = true; // return a shallow copy, because the hash may change
52298 // later if this feature gets merged with another
52300 results.push(Object.assign({}, feature)); // shallow copy
52306 loadTiles: function loadTiles(sourceID, template, projection) {
52307 var source = _vtCache[sourceID];
52310 source = this.addSource(sourceID, template);
52313 var tiles = tiler$7.getTiles(projection); // abort inflight requests that are no longer needed
52315 Object.keys(source.inflight).forEach(function (k) {
52316 var wanted = tiles.find(function (tile) {
52317 return k === tile.id;
52321 abortRequest$7(source.inflight[k]);
52322 delete source.inflight[k];
52325 tiles.forEach(function (tile) {
52326 loadTile(source, tile);
52329 cache: function cache() {
52334 var apibase$3 = 'https://www.wikidata.org/w/api.php?';
52335 var _wikidataCache = {};
52336 var serviceWikidata = {
52337 init: function init() {},
52338 reset: function reset() {
52339 _wikidataCache = {};
52341 // Search for Wikidata items matching the query
52342 itemsForSearchQuery: function itemsForSearchQuery(query, callback) {
52344 if (callback) callback('No query', {});
52348 var lang = this.languagesToQuery()[0];
52349 var url = apibase$3 + utilQsString({
52350 action: 'wbsearchentities',
52355 // the language to search
52357 // the language for the label and description in the result
52362 d3_json(url).then(function (result) {
52363 if (result && result.error) {
52364 throw new Error(result.error);
52367 if (callback) callback(null, result.search || {});
52368 })["catch"](function (err) {
52369 if (callback) callback(err.message, {});
52372 // Given a Wikipedia language and article title,
52373 // return an array of corresponding Wikidata entities.
52374 itemsByTitle: function itemsByTitle(lang, title, callback) {
52376 if (callback) callback('No title', {});
52380 lang = lang || 'en';
52381 var url = apibase$3 + utilQsString({
52382 action: 'wbgetentities',
52385 sites: lang.replace(/-/g, '_') + 'wiki',
52388 // shrink response by filtering to one language
52391 d3_json(url).then(function (result) {
52392 if (result && result.error) {
52393 throw new Error(result.error);
52396 if (callback) callback(null, result.entities || {});
52397 })["catch"](function (err) {
52398 if (callback) callback(err.message, {});
52401 languagesToQuery: function languagesToQuery() {
52402 return _mainLocalizer.localeCodes().map(function (code) {
52403 return code.toLowerCase();
52404 }).filter(function (code) {
52405 // HACK: en-us isn't a wikidata language. We should really be filtering by
52406 // the languages known to be supported by wikidata.
52407 return code !== 'en-us';
52410 entityByQID: function entityByQID(qid, callback) {
52412 callback('No qid', {});
52416 if (_wikidataCache[qid]) {
52417 if (callback) callback(null, _wikidataCache[qid]);
52421 var langs = this.languagesToQuery();
52422 var url = apibase$3 + utilQsString({
52423 action: 'wbgetentities',
52427 props: 'labels|descriptions|claims|sitelinks',
52428 sitefilter: langs.map(function (d) {
52431 languages: langs.join('|'),
52432 languagefallback: 1,
52435 d3_json(url).then(function (result) {
52436 if (result && result.error) {
52437 throw new Error(result.error);
52440 if (callback) callback(null, result.entities[qid] || {});
52441 })["catch"](function (err) {
52442 if (callback) callback(err.message, {});
52445 // Pass `params` object of the form:
52447 // qid: 'string' // brand wikidata (e.g. 'Q37158')
52450 // Get an result object used to display tag documentation
52452 // title: 'string',
52453 // description: 'string',
52454 // editURL: 'string',
52455 // imageURL: 'string',
52456 // wiki: { title: 'string', text: 'string', url: 'string' }
52459 getDocs: function getDocs(params, callback) {
52460 var langs = this.languagesToQuery();
52461 this.entityByQID(params.qid, function (err, entity) {
52462 if (err || !entity) {
52463 callback(err || 'No entity');
52471 var code = langs[i];
52473 if (entity.descriptions[code] && entity.descriptions[code].language === code) {
52474 description = entity.descriptions[code];
52479 if (!description && Object.values(entity.descriptions).length) description = Object.values(entity.descriptions)[0]; // prepare result
52483 description: description ? description.value : '',
52484 descriptionLocaleCode: description ? description.language : '',
52485 editURL: 'https://www.wikidata.org/wiki/' + entity.id
52488 if (entity.claims) {
52489 var imageroot = 'https://commons.wikimedia.org/w/index.php';
52490 var props = ['P154', 'P18']; // logo image, image
52494 for (i = 0; i < props.length; i++) {
52495 prop = entity.claims[props[i]];
52497 if (prop && Object.keys(prop).length > 0) {
52498 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
52501 result.imageURL = imageroot + '?' + utilQsString({
52502 title: 'Special:Redirect/file/' + image,
52511 if (entity.sitelinks) {
52512 var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en'; // must be one of these that we requested..
52514 for (i = 0; i < langs.length; i++) {
52515 // check each, in order of preference
52516 var w = langs[i] + 'wiki';
52518 if (entity.sitelinks[w]) {
52519 var title = entity.sitelinks[w].title;
52520 var tKey = 'inspector.wiki_reference';
52522 if (!englishLocale && langs[i] === 'en') {
52523 // user's locale isn't English but
52524 tKey = 'inspector.wiki_en_reference'; // we are sending them to enwiki anyway..
52530 url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
52537 callback(null, result);
52542 var endpoint = 'https://en.wikipedia.org/w/api.php?';
52543 var serviceWikipedia = {
52544 init: function init() {},
52545 reset: function reset() {},
52546 search: function search(lang, query, callback) {
52548 if (callback) callback('No Query', []);
52552 lang = lang || 'en';
52553 var url = endpoint.replace('en', lang) + utilQsString({
52557 srinfo: 'suggestion',
52562 d3_json(url).then(function (result) {
52563 if (result && result.error) {
52564 throw new Error(result.error);
52565 } else if (!result || !result.query || !result.query.search) {
52566 throw new Error('No Results');
52570 var titles = result.query.search.map(function (d) {
52573 callback(null, titles);
52575 })["catch"](function (err) {
52576 if (callback) callback(err, []);
52579 suggestions: function suggestions(lang, query, callback) {
52581 if (callback) callback('', []);
52585 lang = lang || 'en';
52586 var url = endpoint.replace('en', lang) + utilQsString({
52587 action: 'opensearch',
52594 d3_json(url).then(function (result) {
52595 if (result && result.error) {
52596 throw new Error(result.error);
52597 } else if (!result || result.length < 2) {
52598 throw new Error('No Results');
52601 if (callback) callback(null, result[1] || []);
52602 })["catch"](function (err) {
52603 if (callback) callback(err.message, []);
52606 translations: function translations(lang, title, callback) {
52608 if (callback) callback('No Title');
52612 var url = endpoint.replace('en', lang) + utilQsString({
52620 d3_json(url).then(function (result) {
52621 if (result && result.error) {
52622 throw new Error(result.error);
52623 } else if (!result || !result.query || !result.query.pages) {
52624 throw new Error('No Results');
52628 var list = result.query.pages[Object.keys(result.query.pages)[0]];
52629 var translations = {};
52631 if (list && list.langlinks) {
52632 list.langlinks.forEach(function (d) {
52633 translations[d.lang] = d['*'];
52637 callback(null, translations);
52639 })["catch"](function (err) {
52640 if (callback) callback(err.message);
52646 geocoder: serviceNominatim,
52647 keepRight: serviceKeepRight,
52648 improveOSM: serviceImproveOSM,
52649 osmose: serviceOsmose,
52650 mapillary: serviceMapillary,
52651 openstreetcam: serviceOpenstreetcam,
52653 osmWikibase: serviceOsmWikibase,
52654 maprules: serviceMapRules,
52655 streetside: serviceStreetside,
52656 taginfo: serviceTaginfo,
52657 vectorTile: serviceVectorTile,
52658 wikidata: serviceWikidata,
52659 wikipedia: serviceWikipedia
52662 function svgIcon(name, svgklass, useklass) {
52663 return function drawIcon(selection) {
52664 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);
52668 function uiNoteComments() {
52671 function noteComments(selection) {
52672 if (_note.isNew()) return; // don't draw .comments-container
52674 var comments = selection.selectAll('.comments-container').data([0]);
52675 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments);
52676 var commentEnter = comments.selectAll('.comment').data(_note.comments).enter().append('div').attr('class', 'comment');
52677 commentEnter.append('div').attr('class', function (d) {
52678 return 'comment-avatar user-' + d.uid;
52679 }).call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
52680 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
52681 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
52682 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
52683 var selection = select(this);
52684 var osm = services.osm;
52686 if (osm && d.user) {
52687 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.user)).attr('target', '_blank');
52690 selection.html(function (d) {
52691 return d.user || _t.html('note.anonymous');
52694 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
52695 return _t('note.status.' + d.action, {
52696 when: localeDateString(d.date)
52699 mainEnter.append('div').attr('class', 'comment-text').html(function (d) {
52701 }).selectAll('a').attr('rel', 'noopener nofollow').attr('target', '_blank');
52702 comments.call(replaceAvatars);
52705 function replaceAvatars(selection) {
52706 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
52707 var osm = services.osm;
52708 if (showThirdPartyIcons !== 'true' || !osm) return;
52709 var uids = {}; // gather uids in the comment thread
52711 _note.comments.forEach(function (d) {
52712 if (d.uid) uids[d.uid] = true;
52715 Object.keys(uids).forEach(function (uid) {
52716 osm.loadUser(uid, function (err, user) {
52717 if (!user || !user.image_url) return;
52718 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);
52723 function localeDateString(s) {
52724 if (!s) return null;
52730 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
52732 var d = new Date(s);
52733 if (isNaN(d.getTime())) return null;
52734 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
52737 noteComments.note = function (val) {
52738 if (!arguments.length) return _note;
52740 return noteComments;
52743 return noteComments;
52746 function uiNoteHeader() {
52749 function noteHeader(selection) {
52750 var header = selection.selectAll('.note-header').data(_note ? [_note] : [], function (d) {
52751 return d.status + d.id;
52753 header.exit().remove();
52754 var headerEnter = header.enter().append('div').attr('class', 'note-header');
52755 var iconEnter = headerEnter.append('div').attr('class', function (d) {
52756 return 'note-header-icon ' + d.status;
52757 }).classed('new', function (d) {
52760 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-note', 'note-fill'));
52761 iconEnter.each(function (d) {
52762 var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : d.status === 'open' ? 'close' : 'apply');
52763 iconEnter.append('div').attr('class', 'note-icon-annotation').call(svgIcon(statusIcon, 'icon-annotation'));
52765 headerEnter.append('div').attr('class', 'note-header-label').html(function (d) {
52766 if (_note.isNew()) {
52767 return _t('note.new');
52770 return _t('note.note') + ' ' + d.id + ' ' + (d.status === 'closed' ? _t('note.closed') : '');
52774 noteHeader.note = function (val) {
52775 if (!arguments.length) return _note;
52783 function uiNoteReport() {
52786 function noteReport(selection) {
52789 if (services.osm && _note instanceof osmNote && !_note.isNew()) {
52790 url = services.osm.noteReportURL(_note);
52793 var link = selection.selectAll('.note-report').data(url ? [url] : []); // exit
52795 link.exit().remove(); // enter
52797 var linkEnter = link.enter().append('a').attr('class', 'note-report').attr('target', '_blank').attr('href', function (d) {
52799 }).call(svgIcon('#iD-icon-out-link', 'inline'));
52800 linkEnter.append('span').html(_t.html('note.report'));
52803 noteReport.note = function (val) {
52804 if (!arguments.length) return _note;
52812 function uiViewOnOSM(context) {
52813 var _what; // an osmEntity or osmNote
52816 function viewOnOSM(selection) {
52819 if (_what instanceof osmEntity) {
52820 url = context.connection().entityURL(_what);
52821 } else if (_what instanceof osmNote) {
52822 url = context.connection().noteURL(_what);
52825 var data = !_what || _what.isNew() ? [] : [_what];
52826 var link = selection.selectAll('.view-on-osm').data(data, function (d) {
52830 link.exit().remove(); // enter
52832 var linkEnter = link.enter().append('a').attr('class', 'view-on-osm').attr('target', '_blank').attr('href', url).call(svgIcon('#iD-icon-out-link', 'inline'));
52833 linkEnter.append('span').html(_t.html('inspector.view_on_osm'));
52836 viewOnOSM.what = function (_) {
52837 if (!arguments.length) return _what;
52845 function uiNoteEditor(context) {
52846 var dispatch$1 = dispatch('change');
52847 var noteComments = uiNoteComments();
52848 var noteHeader = uiNoteHeader(); // var formFields = uiFormFields(context);
52852 var _newNote; // var _fieldsArr;
52855 function noteEditor(selection) {
52856 var header = selection.selectAll('.header').data([0]);
52857 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
52858 headerEnter.append('button').attr('class', 'close').on('click', function () {
52859 context.enter(modeBrowse(context));
52860 }).call(svgIcon('#iD-icon-close'));
52861 headerEnter.append('h3').html(_t.html('note.title'));
52862 var body = selection.selectAll('.body').data([0]);
52863 body = body.enter().append('div').attr('class', 'body').merge(body);
52864 var editor = body.selectAll('.note-editor').data([0]);
52865 editor.enter().append('div').attr('class', 'modal-section note-editor').merge(editor).call(noteHeader.note(_note)).call(noteComments.note(_note)).call(noteSaveSection);
52866 var footer = selection.selectAll('.footer').data([0]);
52867 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
52869 var osm = services.osm;
52872 osm.on('change.note-save', function () {
52873 selection.call(noteEditor);
52878 function noteSaveSection(selection) {
52879 var isSelected = _note && _note.id === context.selectedNoteID();
52881 var noteSave = selection.selectAll('.note-save').data(isSelected ? [_note] : [], function (d) {
52882 return d.status + d.id;
52885 noteSave.exit().remove(); // enter
52887 var noteSaveEnter = noteSave.enter().append('div').attr('class', 'note-save save-section cf'); // // if new note, show categories to pick from
52888 // if (_note.isNew()) {
52889 // var presets = presetManager;
52890 // // NOTE: this key isn't a age and therefore there is no documentation (yet)
52892 // uiField(context, presets.field('category'), null, { show: true, revert: false }),
52894 // _fieldsArr.forEach(function(field) {
52896 // .on('change', changeCategory);
52900 // .attr('class', 'note-category')
52901 // .call(formFields.fieldsArr(_fieldsArr));
52903 // function changeCategory() {
52904 // // NOTE: perhaps there is a better way to get value
52905 // var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
52906 // // store the unsaved category with the note itself
52907 // _note = _note.update({ newCategory: val });
52908 // var osm = services.osm;
52910 // osm.replaceNote(_note); // update note cache
52913 // .call(noteSaveButtons);
52916 noteSaveEnter.append('h4').attr('class', '.note-save-header').html(function () {
52917 return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
52919 var commentTextarea = noteSaveEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('note.inputPlaceholder')).attr('maxlength', 1000).property('value', function (d) {
52920 return d.newComment;
52921 }).call(utilNoAuto).on('keydown.note-input', keydown).on('input.note-input', changeInput).on('blur.note-input', changeInput);
52923 if (!commentTextarea.empty() && _newNote) {
52924 // autofocus the comment field for new notes
52925 commentTextarea.node().focus();
52929 noteSave = noteSaveEnter.merge(noteSave).call(userDetails).call(noteSaveButtons); // fast submit if user presses cmd+enter
52931 function keydown(d3_event) {
52932 if (!(d3_event.keyCode === 13 && // ↩ Return
52933 d3_event.metaKey)) return;
52934 var osm = services.osm;
52936 var hasAuth = osm.authenticated();
52937 if (!hasAuth) return;
52938 if (!_note.newComment) return;
52939 d3_event.preventDefault();
52940 select(this).on('keydown.note-input', null); // focus on button and submit
52942 window.setTimeout(function () {
52943 if (_note.isNew()) {
52944 noteSave.selectAll('.save-button').node().focus();
52947 noteSave.selectAll('.comment-button').node().focus();
52953 function changeInput() {
52954 var input = select(this);
52955 var val = input.property('value').trim() || undefined; // store the unsaved comment with the note itself
52957 _note = _note.update({
52960 var osm = services.osm;
52963 osm.replaceNote(_note); // update note cache
52966 noteSave.call(noteSaveButtons);
52970 function userDetails(selection) {
52971 var detailSection = selection.selectAll('.detail-section').data([0]);
52972 detailSection = detailSection.enter().append('div').attr('class', 'detail-section').merge(detailSection);
52973 var osm = services.osm;
52974 if (!osm) return; // Add warning if user is not logged in
52976 var hasAuth = osm.authenticated();
52977 var authWarning = detailSection.selectAll('.auth-warning').data(hasAuth ? [] : [0]);
52978 authWarning.exit().transition().duration(200).style('opacity', 0).remove();
52979 var authEnter = authWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning auth-warning').style('opacity', 0);
52980 authEnter.call(svgIcon('#iD-icon-alert', 'inline'));
52981 authEnter.append('span').html(_t.html('note.login'));
52982 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) {
52983 d3_event.preventDefault();
52984 osm.authenticate();
52986 authEnter.transition().duration(200).style('opacity', 1);
52987 var prose = detailSection.selectAll('.note-save-prose').data(hasAuth ? [0] : []);
52988 prose.exit().remove();
52989 prose = prose.enter().append('p').attr('class', 'note-save-prose').html(_t.html('note.upload_explanation')).merge(prose);
52990 osm.userDetails(function (err, user) {
52992 var userLink = select(document.createElement('div'));
52994 if (user.image_url) {
52995 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
52998 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
52999 prose.html(_t.html('note.upload_explanation_with_user', {
53000 user: userLink.html()
53005 function noteSaveButtons(selection) {
53006 var osm = services.osm;
53007 var hasAuth = osm && osm.authenticated();
53009 var isSelected = _note && _note.id === context.selectedNoteID();
53011 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_note] : [], function (d) {
53012 return d.status + d.id;
53015 buttonSection.exit().remove(); // enter
53017 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
53019 if (_note.isNew()) {
53020 buttonEnter.append('button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
53021 buttonEnter.append('button').attr('class', 'button save-button action').html(_t.html('note.save'));
53023 buttonEnter.append('button').attr('class', 'button status-button action');
53024 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('note.comment'));
53028 buttonSection = buttonSection.merge(buttonEnter);
53029 buttonSection.select('.cancel-button') // select and propagate data
53030 .on('click.cancel', clickCancel);
53031 buttonSection.select('.save-button') // select and propagate data
53032 .attr('disabled', isSaveDisabled).on('click.save', clickSave);
53033 buttonSection.select('.status-button') // select and propagate data
53034 .attr('disabled', hasAuth ? null : true).html(function (d) {
53035 var action = d.status === 'open' ? 'close' : 'open';
53036 var andComment = d.newComment ? '_comment' : '';
53037 return _t('note.' + action + andComment);
53038 }).on('click.status', clickStatus);
53039 buttonSection.select('.comment-button') // select and propagate data
53040 .attr('disabled', isSaveDisabled).on('click.comment', clickComment);
53042 function isSaveDisabled(d) {
53043 return hasAuth && d.status === 'open' && d.newComment ? null : true;
53047 function clickCancel(d3_event, d) {
53048 this.blur(); // avoid keeping focus on the button - #4641
53050 var osm = services.osm;
53056 context.enter(modeBrowse(context));
53057 dispatch$1.call('change');
53060 function clickSave(d3_event, d) {
53061 this.blur(); // avoid keeping focus on the button - #4641
53063 var osm = services.osm;
53066 osm.postNoteCreate(d, function (err, note) {
53067 dispatch$1.call('change', note);
53072 function clickStatus(d3_event, d) {
53073 this.blur(); // avoid keeping focus on the button - #4641
53075 var osm = services.osm;
53078 var setStatus = d.status === 'open' ? 'closed' : 'open';
53079 osm.postNoteUpdate(d, setStatus, function (err, note) {
53080 dispatch$1.call('change', note);
53085 function clickComment(d3_event, d) {
53086 this.blur(); // avoid keeping focus on the button - #4641
53088 var osm = services.osm;
53091 osm.postNoteUpdate(d, d.status, function (err, note) {
53092 dispatch$1.call('change', note);
53097 noteEditor.note = function (val) {
53098 if (!arguments.length) return _note;
53103 noteEditor.newNote = function (val) {
53104 if (!arguments.length) return _newNote;
53109 return utilRebind(noteEditor, dispatch$1, 'on');
53112 function modeSelectNote(context, selectedNoteID) {
53118 var _keybinding = utilKeybinding('select-note');
53120 var _noteEditor = uiNoteEditor(context).on('change', function () {
53121 context.map().pan([0, 0]); // trigger a redraw
53123 var note = checkSelectedID();
53125 context.ui().sidebar.show(_noteEditor.note(note));
53128 var _behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
53129 var _newFeature = false;
53131 function checkSelectedID() {
53132 if (!services.osm) return;
53133 var note = services.osm.getNote(selectedNoteID);
53136 context.enter(modeBrowse(context));
53140 } // class the note as selected, or return to browse mode if the note is gone
53143 function selectNote(d3_event, drawn) {
53144 if (!checkSelectedID()) return;
53145 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
53147 if (selection.empty()) {
53148 // Return to browse mode if selected DOM elements have
53149 // disappeared because the user moved them out of view..
53150 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
53152 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
53153 context.enter(modeBrowse(context));
53156 selection.classed('selected', true);
53157 context.selectedNoteID(selectedNoteID);
53162 if (context.container().select('.combobox').size()) return;
53163 context.enter(modeBrowse(context));
53166 mode.zoomToSelected = function () {
53167 if (!services.osm) return;
53168 var note = services.osm.getNote(selectedNoteID);
53171 context.map().centerZoomEase(note.loc, 20);
53175 mode.newFeature = function (val) {
53176 if (!arguments.length) return _newFeature;
53181 mode.enter = function () {
53182 var note = checkSelectedID();
53185 _behaviors.forEach(context.install);
53187 _keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
53189 select(document).call(_keybinding);
53191 var sidebar = context.ui().sidebar;
53192 sidebar.show(_noteEditor.note(note).newNote(_newFeature)); // expand the sidebar, avoid obscuring the note if needed
53194 sidebar.expand(sidebar.intersects(note.extent()));
53195 context.map().on('drawn.select', selectNote);
53198 mode.exit = function () {
53199 _behaviors.forEach(context.uninstall);
53201 select(document).call(_keybinding.unbind);
53202 context.surface().selectAll('.layer-notes .selected').classed('selected hover', false);
53203 context.map().on('drawn.select', null);
53204 context.ui().sidebar.hide();
53205 context.selectedNoteID(null);
53211 function modeDragNote(context) {
53216 var edit = behaviorEdit(context);
53218 var _nudgeInterval;
53222 var _note; // most current note.. dragged note may have stale datum.
53225 function startNudge(d3_event, nudge) {
53226 if (_nudgeInterval) window.clearInterval(_nudgeInterval);
53227 _nudgeInterval = window.setInterval(function () {
53228 context.map().pan(nudge);
53229 doMove(d3_event, nudge);
53233 function stopNudge() {
53234 if (_nudgeInterval) {
53235 window.clearInterval(_nudgeInterval);
53236 _nudgeInterval = null;
53240 function origin(note) {
53241 return context.projection(note.loc);
53244 function start(d3_event, note) {
53246 var osm = services.osm;
53249 // Get latest note from cache.. The marker may have a stale datum bound to it
53250 // and dragging it around can sometimes delete the users note comment.
53251 _note = osm.getNote(_note.id);
53254 context.surface().selectAll('.note-' + _note.id).classed('active', true);
53255 context.perform(actionNoop());
53256 context.enter(mode);
53257 context.selectedNoteID(_note.id);
53260 function move(d3_event, entity, point) {
53261 d3_event.stopPropagation();
53262 _lastLoc = context.projection.invert(point);
53264 var nudge = geoViewportEdge(point, context.map().dimensions());
53267 startNudge(d3_event, nudge);
53273 function doMove(d3_event, nudge) {
53274 nudge = nudge || [0, 0];
53275 var currPoint = d3_event && d3_event.point || context.projection(_lastLoc);
53276 var currMouse = geoVecSubtract(currPoint, nudge);
53277 var loc = context.projection.invert(currMouse);
53278 _note = _note.move(loc);
53279 var osm = services.osm;
53282 osm.replaceNote(_note); // update note cache
53285 context.replace(actionNoop()); // trigger redraw
53289 context.replace(actionNoop()); // trigger redraw
53291 context.selectedNoteID(_note.id).enter(modeSelectNote(context, _note.id));
53294 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);
53296 mode.enter = function () {
53297 context.install(edit);
53300 mode.exit = function () {
53301 context.ui().sidebar.hover.cancel();
53302 context.uninstall(edit);
53303 context.surface().selectAll('.active').classed('active', false);
53307 mode.behavior = drag;
53311 function uiDataHeader() {
53314 function dataHeader(selection) {
53315 var header = selection.selectAll('.data-header').data(_datum ? [_datum] : [], function (d) {
53316 return d.__featurehash__;
53318 header.exit().remove();
53319 var headerEnter = header.enter().append('div').attr('class', 'data-header');
53320 var iconEnter = headerEnter.append('div').attr('class', 'data-header-icon');
53321 iconEnter.append('div').attr('class', 'preset-icon-28').call(svgIcon('#iD-icon-data', 'note-fill'));
53322 headerEnter.append('div').attr('class', 'data-header-label').html(_t.html('map_data.layers.custom.title'));
53325 dataHeader.datum = function (val) {
53326 if (!arguments.length) return _datum;
53334 // It is keyed on the `value` of the entry. Data should be an array of objects like:
53336 // value: 'string value', // required
53337 // display: 'label html' // optional
53338 // title: 'hover text' // optional
53339 // terms: ['search terms'] // optional
53342 var _comboHideTimerID;
53344 function uiCombobox(context, klass) {
53345 var dispatch$1 = dispatch('accept', 'cancel');
53346 var container = context.container();
53347 var _suggestions = [];
53350 var _selected = null;
53351 var _canAutocomplete = true;
53352 var _caseSensitive = false;
53353 var _cancelFetch = false;
53357 var _mouseEnterHandler, _mouseLeaveHandler;
53359 var _fetcher = function _fetcher(val, cb) {
53360 cb(_data.filter(function (d) {
53361 var terms = d.terms || [];
53362 terms.push(d.value);
53363 return terms.some(function (term) {
53364 return term.toString().toLowerCase().indexOf(val.toLowerCase()) !== -1;
53369 var combobox = function combobox(input, attachTo) {
53370 if (!input || input.empty()) return;
53371 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 () {
53372 var parent = this.parentNode;
53373 var sibling = this.nextSibling;
53374 select(parent).selectAll('.combobox-caret').filter(function (d) {
53375 return d === input.node();
53376 }).data([input.node()]).enter().insert('div', function () {
53378 }).attr('class', 'combobox-caret').on('mousedown.combo-caret', function (d3_event) {
53379 d3_event.preventDefault(); // don't steal focus from input
53381 input.node().focus(); // focus the input as if it was clicked
53383 mousedown(d3_event);
53384 }).on('mouseup.combo-caret', function (d3_event) {
53385 d3_event.preventDefault(); // don't steal focus from input
53391 function mousedown(d3_event) {
53392 if (d3_event.button !== 0) return; // left click only
53394 _tDown = +new Date(); // clear selection
53396 var start = input.property('selectionStart');
53397 var end = input.property('selectionEnd');
53399 if (start !== end) {
53400 var val = utilGetSetValue(input);
53401 input.node().setSelectionRange(val.length, val.length);
53405 input.on('mouseup.combo-input', mouseup);
53408 function mouseup(d3_event) {
53409 input.on('mouseup.combo-input', null);
53410 if (d3_event.button !== 0) return; // left click only
53412 if (input.node() !== document.activeElement) return; // exit if this input is not focused
53414 var start = input.property('selectionStart');
53415 var end = input.property('selectionEnd');
53416 if (start !== end) return; // exit if user is selecting
53417 // not showing or showing for a different field - try to show it.
53419 var combo = container.selectAll('.combobox');
53421 if (combo.empty() || combo.datum() !== input.node()) {
53422 var tOrig = _tDown;
53423 window.setTimeout(function () {
53424 if (tOrig !== _tDown) return; // exit if user double clicked
53426 fetchComboData('', function () {
53437 fetchComboData(''); // prefetch values (may warm taginfo cache)
53441 _comboHideTimerID = window.setTimeout(hide, 75);
53445 hide(); // remove any existing
53447 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) {
53448 // prevent moving focus out of the input field
53449 d3_event.preventDefault();
53451 container.on('scroll.combo-scroll', render, true);
53455 if (_comboHideTimerID) {
53456 window.clearTimeout(_comboHideTimerID);
53457 _comboHideTimerID = undefined;
53460 container.selectAll('.combobox').remove();
53461 container.on('scroll.combo-scroll', null);
53464 function keydown(d3_event) {
53465 var shown = !container.selectAll('.combobox').empty();
53466 var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
53468 switch (d3_event.keyCode) {
53469 case 8: // ⌫ Backspace
53473 d3_event.stopPropagation();
53476 input.on('input.combo-input', function () {
53477 var start = input.property('selectionStart');
53478 input.node().setSelectionRange(start, start);
53479 input.on('input.combo-input', change);
53490 d3_event.preventDefault();
53491 d3_event.stopPropagation();
53496 if (tagName === 'textarea' && !shown) return;
53497 d3_event.preventDefault();
53499 if (tagName === 'input' && !shown) {
53508 if (tagName === 'textarea' && !shown) return;
53509 d3_event.preventDefault();
53511 if (tagName === 'input' && !shown) {
53520 function keyup(d3_event) {
53521 switch (d3_event.keyCode) {
53532 } // Called whenever the input value is changed (e.g. on typing)
53535 function change() {
53536 fetchComboData(value(), function () {
53538 var val = input.property('value');
53540 if (_suggestions.length) {
53541 if (input.property('selectionEnd') === val.length) {
53542 _selected = tryAutocomplete();
53551 var combo = container.selectAll('.combobox');
53553 if (combo.empty()) {
53562 } // Called when the user presses up/down arrows to navigate the list
53565 function nav(dir) {
53566 if (_suggestions.length) {
53567 // try to determine previously selected index..
53570 for (var i = 0; i < _suggestions.length; i++) {
53571 if (_selected && _suggestions[i].value === _selected) {
53575 } // pick new _selected
53578 index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
53579 _selected = _suggestions[index].value;
53580 input.property('value', _selected);
53587 function ensureVisible() {
53588 var combo = container.selectAll('.combobox');
53589 if (combo.empty()) return;
53590 var containerRect = container.node().getBoundingClientRect();
53591 var comboRect = combo.node().getBoundingClientRect();
53593 if (comboRect.bottom > containerRect.bottom) {
53594 var node = attachTo ? attachTo.node() : input.node();
53595 node.scrollIntoView({
53596 behavior: 'instant',
53600 } // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
53603 var selected = combo.selectAll('.combobox-option.selected').node();
53606 selected.scrollIntoView({
53607 behavior: 'smooth',
53614 var value = input.property('value');
53615 var start = input.property('selectionStart');
53616 var end = input.property('selectionEnd');
53618 if (start && end) {
53619 value = value.substring(0, start);
53625 function fetchComboData(v, cb) {
53626 _cancelFetch = false;
53628 _fetcher.call(input, v, function (results) {
53629 // already chose a value, don't overwrite or autocomplete it
53630 if (_cancelFetch) return;
53631 _suggestions = results;
53632 results.forEach(function (d) {
53633 _fetched[d.value] = d;
53642 function tryAutocomplete() {
53643 if (!_canAutocomplete) return;
53644 var val = _caseSensitive ? value() : value().toLowerCase();
53645 if (!val) return; // Don't autocomplete if user is typing a number - #4935
53647 if (!isNaN(parseFloat(val)) && isFinite(val)) return;
53648 var bestIndex = -1;
53650 for (var i = 0; i < _suggestions.length; i++) {
53651 var suggestion = _suggestions[i].value;
53652 var compare = _caseSensitive ? suggestion : suggestion.toLowerCase(); // if search string matches suggestion exactly, pick it..
53654 if (compare === val) {
53656 break; // otherwise lock in the first result that starts with the search string..
53657 } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
53662 if (bestIndex !== -1) {
53663 var bestVal = _suggestions[bestIndex].value;
53664 input.property('value', bestVal);
53665 input.node().setSelectionRange(val.length, bestVal.length);
53670 function render() {
53671 if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
53676 var shown = !container.selectAll('.combobox').empty();
53677 if (!shown) return;
53678 var combo = container.selectAll('.combobox');
53679 var options = combo.selectAll('.combobox-option').data(_suggestions, function (d) {
53682 options.exit().remove(); // enter/update
53684 options.enter().append('a').attr('class', 'combobox-option').attr('title', function (d) {
53686 }).html(function (d) {
53687 return d.display || d.value;
53688 }).on('mouseenter', _mouseEnterHandler).on('mouseleave', _mouseLeaveHandler).merge(options).classed('selected', function (d) {
53689 return d.value === _selected;
53690 }).on('click.combo-option', accept).order();
53691 var node = attachTo ? attachTo.node() : input.node();
53692 var containerRect = container.node().getBoundingClientRect();
53693 var rect = node.getBoundingClientRect();
53694 combo.style('left', rect.left + 5 - containerRect.left + 'px').style('width', rect.width - 10 + 'px').style('top', rect.height + rect.top - containerRect.top + 'px');
53695 } // Dispatches an 'accept' event
53696 // Then hides the combobox.
53699 function accept(d3_event, d) {
53700 _cancelFetch = true;
53701 var thiz = input.node();
53704 // user clicked on a suggestion
53705 utilGetSetValue(input, d.value); // replace field contents
53707 utilTriggerEvent(input, 'change');
53708 } // clear (and keep) selection
53711 var val = utilGetSetValue(input);
53712 thiz.setSelectionRange(val.length, val.length);
53714 dispatch$1.call('accept', thiz, d, val);
53716 } // Dispatches an 'cancel' event
53717 // Then hides the combobox.
53720 function cancel() {
53721 _cancelFetch = true;
53722 var thiz = input.node(); // clear (and remove) selection, and replace field contents
53724 var val = utilGetSetValue(input);
53725 var start = input.property('selectionStart');
53726 var end = input.property('selectionEnd');
53727 val = val.slice(0, start) + val.slice(end);
53728 utilGetSetValue(input, val);
53729 thiz.setSelectionRange(val.length, val.length);
53730 dispatch$1.call('cancel', thiz);
53735 combobox.canAutocomplete = function (val) {
53736 if (!arguments.length) return _canAutocomplete;
53737 _canAutocomplete = val;
53741 combobox.caseSensitive = function (val) {
53742 if (!arguments.length) return _caseSensitive;
53743 _caseSensitive = val;
53747 combobox.data = function (val) {
53748 if (!arguments.length) return _data;
53753 combobox.fetcher = function (val) {
53754 if (!arguments.length) return _fetcher;
53759 combobox.minItems = function (val) {
53760 if (!arguments.length) return _minItems;
53765 combobox.itemsMouseEnter = function (val) {
53766 if (!arguments.length) return _mouseEnterHandler;
53767 _mouseEnterHandler = val;
53771 combobox.itemsMouseLeave = function (val) {
53772 if (!arguments.length) return _mouseLeaveHandler;
53773 _mouseLeaveHandler = val;
53777 return utilRebind(combobox, dispatch$1, 'on');
53780 uiCombobox.off = function (input, context) {
53781 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);
53782 context.container().on('scroll.combo-scroll', null);
53785 // hide class, which sets display=none, and a d3 transition for opacity.
53786 // this will cause blinking when called repeatedly, so check that the
53787 // value actually changes between calls.
53789 function uiToggle(show, callback) {
53790 return function (selection) {
53791 selection.style('opacity', show ? 0 : 1).classed('hide', false).transition().style('opacity', show ? 1 : 0).on('end', function () {
53792 select(this).classed('hide', !show).style('opacity', null);
53793 if (callback) callback.apply(this);
53798 function uiDisclosure(context, key, expandedDefault) {
53799 var dispatch$1 = dispatch('toggled');
53803 var _label = utilFunctor('');
53805 var _updatePreference = true;
53807 var _content = function _content() {};
53809 var disclosure = function disclosure(selection) {
53810 if (_expanded === undefined || _expanded === null) {
53811 // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
53812 var preference = corePreferences('disclosure.' + key + '.expanded');
53813 _expanded = preference === null ? !!expandedDefault : preference === 'true';
53816 var hideToggle = selection.selectAll('.hide-toggle-' + key).data([0]); // enter
53818 var hideToggleEnter = hideToggle.enter().append('a').attr('href', '#').attr('class', 'hide-toggle hide-toggle-' + key).call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
53819 hideToggleEnter.append('span').attr('class', 'hide-toggle-text'); // update
53821 hideToggle = hideToggleEnter.merge(hideToggle);
53822 hideToggle.on('click', toggle).classed('expanded', _expanded);
53823 hideToggle.selectAll('.hide-toggle-text').html(_label());
53824 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
53825 var wrap = selection.selectAll('.disclosure-wrap').data([0]); // enter/update
53827 wrap = wrap.enter().append('div').attr('class', 'disclosure-wrap disclosure-wrap-' + key).merge(wrap).classed('hide', !_expanded);
53830 wrap.call(_content);
53833 function toggle(d3_event) {
53834 d3_event.preventDefault();
53835 _expanded = !_expanded;
53837 if (_updatePreference) {
53838 corePreferences('disclosure.' + key + '.expanded', _expanded);
53841 hideToggle.classed('expanded', _expanded);
53842 hideToggle.selectAll('.hide-toggle-icon').attr('xlink:href', _expanded ? '#iD-icon-down' : _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
53843 wrap.call(uiToggle(_expanded));
53846 wrap.call(_content);
53849 dispatch$1.call('toggled', this, _expanded);
53853 disclosure.label = function (val) {
53854 if (!arguments.length) return _label;
53855 _label = utilFunctor(val);
53859 disclosure.expanded = function (val) {
53860 if (!arguments.length) return _expanded;
53865 disclosure.updatePreference = function (val) {
53866 if (!arguments.length) return _updatePreference;
53867 _updatePreference = val;
53871 disclosure.content = function (val) {
53872 if (!arguments.length) return _content;
53877 return utilRebind(disclosure, dispatch$1, 'on');
53880 // Can be labeled and collapsible.
53882 function uiSection(id, context) {
53883 var _classes = utilFunctor('');
53885 var _shouldDisplay;
53893 var _expandedByDefault = utilFunctor(true);
53895 var _disclosureContent;
53897 var _disclosureExpanded;
53899 var _containerSelection = select(null);
53905 section.classes = function (val) {
53906 if (!arguments.length) return _classes;
53907 _classes = utilFunctor(val);
53911 section.label = function (val) {
53912 if (!arguments.length) return _label;
53913 _label = utilFunctor(val);
53917 section.expandedByDefault = function (val) {
53918 if (!arguments.length) return _expandedByDefault;
53919 _expandedByDefault = utilFunctor(val);
53923 section.shouldDisplay = function (val) {
53924 if (!arguments.length) return _shouldDisplay;
53925 _shouldDisplay = utilFunctor(val);
53929 section.content = function (val) {
53930 if (!arguments.length) return _content;
53935 section.disclosureContent = function (val) {
53936 if (!arguments.length) return _disclosureContent;
53937 _disclosureContent = val;
53941 section.disclosureExpanded = function (val) {
53942 if (!arguments.length) return _disclosureExpanded;
53943 _disclosureExpanded = val;
53945 }; // may be called multiple times
53948 section.render = function (selection) {
53949 _containerSelection = selection.selectAll('.section-' + id).data([0]);
53951 var sectionEnter = _containerSelection.enter().append('div').attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
53953 _containerSelection = sectionEnter.merge(_containerSelection);
53955 _containerSelection.call(renderContent);
53958 section.reRender = function () {
53959 _containerSelection.call(renderContent);
53962 section.selection = function () {
53963 return _containerSelection;
53966 section.disclosure = function () {
53967 return _disclosure;
53968 }; // may be called multiple times
53971 function renderContent(selection) {
53972 if (_shouldDisplay) {
53973 var shouldDisplay = _shouldDisplay();
53975 selection.classed('hide', !shouldDisplay);
53977 if (!shouldDisplay) {
53978 selection.html('');
53983 if (_disclosureContent) {
53984 if (!_disclosure) {
53985 _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault()).label(_label || '')
53986 /*.on('toggled', function(expanded) {
53987 if (expanded) { selection.node().parentNode.scrollTop += 200; }
53989 .content(_disclosureContent);
53992 if (_disclosureExpanded !== undefined) {
53993 _disclosure.expanded(_disclosureExpanded);
53995 _disclosureExpanded = undefined;
53998 selection.call(_disclosure);
54003 selection.call(_content);
54011 // key: 'string', // required
54012 // value: 'string' // optional
54016 // qid: 'string' // brand wikidata (e.g. 'Q37158')
54020 function uiTagReference(what) {
54021 var wikibase = what.qid ? services.wikidata : services.osmWikibase;
54022 var tagReference = {};
54024 var _button = select(null);
54026 var _body = select(null);
54033 if (!wikibase) return;
54035 _button.classed('tag-reference-loading', true);
54037 wikibase.getDocs(what, gotDocs);
54040 function gotDocs(err, docs) {
54043 if (!docs || !docs.title) {
54044 _body.append('p').attr('class', 'tag-reference-description').html(_t.html('inspector.no_documentation_key'));
54050 if (docs.imageURL) {
54051 _body.append('img').attr('class', 'tag-reference-wiki-image').attr('src', docs.imageURL).on('load', function () {
54053 }).on('error', function () {
54054 select(this).remove();
54061 _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'));
54064 _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));
54065 } // Add link to info about "good changeset comments" - #2923
54068 if (what.key === 'comment') {
54069 _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'));
54076 _button.classed('tag-reference-loading', false);
54078 _body.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1');
54082 _button.selectAll('svg.icon use').each(function () {
54083 var iconUse = select(this);
54085 if (iconUse.attr('href') === '#iD-icon-info') {
54086 iconUse.attr('href', '#iD-icon-info-filled');
54092 _body.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
54093 _body.classed('expanded', false);
54098 _button.selectAll('svg.icon use').each(function () {
54099 var iconUse = select(this);
54101 if (iconUse.attr('href') === '#iD-icon-info-filled') {
54102 iconUse.attr('href', '#iD-icon-info');
54107 tagReference.button = function (selection, klass, iconName) {
54108 _button = selection.selectAll('.tag-reference-button').data([0]);
54109 _button = _button.enter().append('button').attr('class', 'tag-reference-button ' + (klass || '')).attr('title', _t('icons.information')).call(svgIcon('#iD-icon-' + (iconName || 'inspect'))).merge(_button);
54111 _button.on('click', function (d3_event) {
54112 d3_event.stopPropagation();
54113 d3_event.preventDefault();
54114 this.blur(); // avoid keeping focus on the button - #4641
54118 } else if (_loaded) {
54126 tagReference.body = function (selection) {
54127 var itemID = what.qid || what.key + '-' + (what.value || '');
54128 _body = selection.selectAll('.tag-reference-body').data([itemID], function (d) {
54132 _body.exit().remove();
54134 _body = _body.enter().append('div').attr('class', 'tag-reference-body').style('max-height', '0').style('opacity', '0').merge(_body);
54136 if (_showing === false) {
54141 tagReference.showing = function (val) {
54142 if (!arguments.length) return _showing;
54144 return tagReference;
54147 return tagReference;
54150 function uiSectionRawTagEditor(id, context) {
54151 var section = uiSection(id, context).classes('raw-tag-editor').label(function () {
54152 var count = Object.keys(_tags).filter(function (d) {
54155 return _t('inspector.title_count', {
54156 title: _t.html('inspector.tags'),
54159 }).expandedByDefault(false).disclosureContent(renderDisclosureContent);
54160 var taginfo = services.taginfo;
54161 var dispatch$1 = dispatch('change');
54162 var availableViews = [{
54164 icon: '#fas-th-list'
54167 icon: '#fas-i-cursor'
54170 var _tagView = corePreferences('raw-tag-editor-view') || 'list'; // 'list, 'text'
54173 var _readOnlyTags = []; // the keys in the order we want them to display
54175 var _orderedKeys = [];
54176 var _showBlank = false;
54177 var _pendingChange = null;
54187 var _didInteract = false;
54189 function interacted() {
54190 _didInteract = true;
54193 function renderDisclosureContent(wrap) {
54194 // remove deleted keys
54195 _orderedKeys = _orderedKeys.filter(function (key) {
54196 return _tags[key] !== undefined;
54197 }); // When switching to a different entity or changing the state (hover/select)
54198 // reorder the keys alphabetically.
54199 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
54200 // Otherwise leave their order alone - #5857, #5927
54202 var all = Object.keys(_tags).sort();
54203 var missingKeys = utilArrayDifference(all, _orderedKeys);
54205 for (var i in missingKeys) {
54206 _orderedKeys.push(missingKeys[i]);
54207 } // assemble row data
54210 var rowData = _orderedKeys.map(function (key, i) {
54216 }); // append blank row last, if necessary
54219 if (!rowData.length || _showBlank) {
54220 _showBlank = false;
54222 index: rowData.length,
54229 var options = wrap.selectAll('.raw-tag-options').data([0]);
54230 options.exit().remove();
54231 var optionsEnter = options.enter().insert('div', ':first-child').attr('class', 'raw-tag-options');
54232 var optionEnter = optionsEnter.selectAll('.raw-tag-option').data(availableViews, function (d) {
54235 optionEnter.append('button').attr('class', function (d) {
54236 return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
54237 }).attr('title', function (d) {
54238 return _t('icons.' + d.id);
54239 }).on('click', function (d3_event, d) {
54241 corePreferences('raw-tag-editor-view', d.id);
54242 wrap.selectAll('.raw-tag-option').classed('selected', function (datum) {
54243 return datum === d;
54245 wrap.selectAll('.tag-text').classed('hide', d.id !== 'text').each(setTextareaHeight);
54246 wrap.selectAll('.tag-list, .add-row').classed('hide', d.id !== 'list');
54247 }).each(function (d) {
54248 select(this).call(svgIcon(d.icon));
54249 }); // View as Text
54251 var textData = rowsToText(rowData);
54252 var textarea = wrap.selectAll('.tag-text').data([0]);
54253 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);
54254 textarea.call(utilGetSetValue, textData).each(setTextareaHeight).on('input', setTextareaHeight).on('focus', interacted).on('blur', textChanged).on('change', textChanged); // View as List
54256 var list = wrap.selectAll('.tag-list').data([0]);
54257 list = list.enter().append('ul').attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : '')).merge(list); // Container for the Add button
54259 var addRowEnter = wrap.selectAll('.add-row').data([0]).enter().append('div').attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
54260 addRowEnter.append('button').attr('class', 'add-tag').call(svgIcon('#iD-icon-plus', 'light')).on('click', addTag);
54261 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
54263 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
54266 var items = list.selectAll('.tag-row').data(rowData, function (d) {
54269 items.exit().each(unbind).remove(); // Enter
54271 var itemsEnter = items.enter().append('li').attr('class', 'tag-row').classed('readonly', isReadOnly);
54272 var innerWrap = itemsEnter.append('div').attr('class', 'inner-wrap');
54273 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);
54274 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);
54275 innerWrap.append('button').attr('class', 'form-field-button remove').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete')); // Update
54277 items = items.merge(itemsEnter).sort(function (a, b) {
54278 return a.index - b.index;
54280 items.each(function (d) {
54281 var row = select(this);
54282 var key = row.select('input.key'); // propagate bound data
54284 var value = row.select('input.value'); // propagate bound data
54286 if (_entityIDs && taginfo && _state !== 'hover') {
54287 bindTypeahead(key, value);
54290 var referenceOptions = {
54294 if (typeof d.value === 'string') {
54295 referenceOptions.value = d.value;
54298 var reference = uiTagReference(referenceOptions);
54300 if (_state === 'hover') {
54301 reference.showing(false);
54304 row.select('.inner-wrap') // propagate bound data
54305 .call(reference.button);
54306 row.call(reference.body);
54307 row.select('button.remove'); // propagate bound data
54309 items.selectAll('input.key').attr('title', function (d) {
54311 }).call(utilGetSetValue, function (d) {
54313 }).attr('readonly', function (d) {
54314 return isReadOnly(d) || typeof d.value !== 'string' || null;
54316 items.selectAll('input.value').attr('title', function (d) {
54317 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
54318 }).classed('mixed', function (d) {
54319 return Array.isArray(d.value);
54320 }).attr('placeholder', function (d) {
54321 return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
54322 }).call(utilGetSetValue, function (d) {
54323 return typeof d.value === 'string' ? d.value : '';
54324 }).attr('readonly', function (d) {
54325 return isReadOnly(d) || null;
54327 items.selectAll('button.remove').on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag); // 'click' fires too late - #5878
54330 function isReadOnly(d) {
54331 for (var i = 0; i < _readOnlyTags.length; i++) {
54332 if (d.key.match(_readOnlyTags[i]) !== null) {
54340 function setTextareaHeight() {
54341 if (_tagView !== 'text') return;
54342 var selection = select(this);
54343 var matches = selection.node().value.match(/\n/g);
54344 var lineCount = 2 + Number(matches && matches.length);
54345 var lineHeight = 20;
54346 selection.style('height', lineCount * lineHeight + 'px');
54349 function stringify(s) {
54350 return JSON.stringify(s).slice(1, -1); // without leading/trailing "
54353 function unstringify(s) {
54357 if (s.length < 1 || s.charAt(0) !== '"') {
54361 if (s.length < 2 || s.charAt(s.length - 1) !== '"' || s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\') {
54365 return JSON.parse(leading + s + trailing);
54368 function rowsToText(rows) {
54369 var str = rows.filter(function (row) {
54370 return row.key && row.key.trim() !== '';
54371 }).map(function (row) {
54372 var rawVal = row.value;
54373 if (typeof rawVal !== 'string') rawVal = '*';
54374 var val = rawVal ? stringify(rawVal) : '';
54375 return stringify(row.key) + '=' + val;
54378 if (_state !== 'hover' && str.length) {
54385 function textChanged() {
54386 var newText = this.value.trim();
54388 newText.split('\n').forEach(function (row) {
54389 var m = row.match(/^\s*([^=]+)=(.*)$/);
54392 var k = context.cleanTagKey(unstringify(m[1].trim()));
54393 var v = context.cleanTagValue(unstringify(m[2].trim()));
54397 var tagDiff = utilTagDiff(_tags, newTags);
54398 if (!tagDiff.length) return;
54399 _pendingChange = _pendingChange || {};
54400 tagDiff.forEach(function (change) {
54403 })) return; // skip unchanged multiselection placeholders
54405 if (change.newVal === '*' && typeof change.oldVal !== 'string') return;
54407 if (change.type === '-') {
54408 _pendingChange[change.key] = undefined;
54409 } else if (change.type === '+') {
54410 _pendingChange[change.key] = change.newVal || '';
54414 if (Object.keys(_pendingChange).length === 0) {
54415 _pendingChange = null;
54422 function pushMore(d3_event) {
54423 // if pressing Tab on the last value field with content, add a blank row
54424 if (d3_event.keyCode === 9 && !d3_event.shiftKey && section.selection().selectAll('.tag-list li:last-child input.value').node() === this && utilGetSetValue(select(this))) {
54429 function bindTypeahead(key, value) {
54430 if (isReadOnly(key.datum())) return;
54432 if (Array.isArray(value.datum().value)) {
54433 value.call(uiCombobox(context, 'tag-value').minItems(1).fetcher(function (value, callback) {
54434 var keyString = utilGetSetValue(key);
54435 if (!_tags[keyString]) return;
54437 var data = _tags[keyString].filter(Boolean).map(function (tagValue) {
54449 var geometry = context.graph().geometry(_entityIDs[0]);
54450 key.call(uiCombobox(context, 'tag-key').fetcher(function (value, callback) {
54453 geometry: geometry,
54455 }, function (err, data) {
54457 var filtered = data.filter(function (d) {
54458 return _tags[d.value] === undefined;
54460 callback(sort(value, filtered));
54464 value.call(uiCombobox(context, 'tag-value').fetcher(function (value, callback) {
54467 key: utilGetSetValue(key),
54468 geometry: geometry,
54470 }, function (err, data) {
54471 if (!err) callback(sort(value, data));
54475 function sort(value, data) {
54476 var sameletter = [];
54479 for (var i = 0; i < data.length; i++) {
54480 if (data[i].value.substring(0, value.length) === value) {
54481 sameletter.push(data[i]);
54483 other.push(data[i]);
54487 return sameletter.concat(other);
54491 function unbind() {
54492 var row = select(this);
54493 row.selectAll('input.key').call(uiCombobox.off, context);
54494 row.selectAll('input.value').call(uiCombobox.off, context);
54497 function keyChange(d3_event, d) {
54498 if (select(this).attr('readonly')) return;
54499 var kOld = d.key; // exit if we are currently about to delete this row anyway - #6366
54501 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) return;
54502 var kNew = context.cleanTagKey(this.value.trim()); // allow no change if the key should be readonly
54511 if (kNew && kNew !== kOld && _tags[kNew] !== undefined) {
54512 // new key is already in use, switch focus to the existing row
54513 this.value = kOld; // reset the key
54515 section.selection().selectAll('.tag-list input.value').each(function (d) {
54516 if (d.key === kNew) {
54517 // send focus to that other value combo instead
54518 var input = select(this).node();
54526 var row = this.parentNode.parentNode;
54527 var inputVal = select(row).selectAll('input.value');
54528 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
54529 _pendingChange = _pendingChange || {};
54532 _pendingChange[kOld] = undefined;
54535 _pendingChange[kNew] = vNew; // update the ordered key index so this row doesn't change position
54537 var existingKeyIndex = _orderedKeys.indexOf(kOld);
54539 if (existingKeyIndex !== -1) _orderedKeys[existingKeyIndex] = kNew;
54540 d.key = kNew; // update datum to avoid exit/enter on tag update
54544 utilGetSetValue(inputVal, vNew);
54548 function valueChange(d3_event, d) {
54549 if (isReadOnly(d)) return; // exit if this is a multiselection and no value was entered
54551 if (typeof d.value !== 'string' && !this.value) return; // exit if we are currently about to delete this row anyway - #6366
54553 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) return;
54554 _pendingChange = _pendingChange || {};
54555 _pendingChange[d.key] = context.cleanTagValue(this.value);
54559 function removeTag(d3_event, d) {
54560 if (isReadOnly(d)) return;
54562 if (d.key === '') {
54563 // removing the blank row
54564 _showBlank = false;
54565 section.reRender();
54567 // remove the key from the ordered key index
54568 _orderedKeys = _orderedKeys.filter(function (key) {
54569 return key !== d.key;
54571 _pendingChange = _pendingChange || {};
54572 _pendingChange[d.key] = undefined;
54577 function addTag() {
54578 // Delay render in case this click is blurring an edited combo.
54579 // Without the setTimeout, the `content` render would wipe out the pending tag change.
54580 window.setTimeout(function () {
54582 section.reRender();
54583 section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
54587 function scheduleChange() {
54588 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
54589 var entityIDs = _entityIDs; // Delay change in case this change is blurring an edited combo. - #5878
54591 window.setTimeout(function () {
54592 if (!_pendingChange) return;
54593 dispatch$1.call('change', this, entityIDs, _pendingChange);
54594 _pendingChange = null;
54598 section.state = function (val) {
54599 if (!arguments.length) return _state;
54601 if (_state !== val) {
54609 section.presets = function (val) {
54610 if (!arguments.length) return _presets;
54613 if (_presets && _presets.length && _presets[0].isFallback()) {
54614 section.disclosureExpanded(true); // don't collapse the disclosure if the mapper used the raw tag editor - #1881
54615 } else if (!_didInteract) {
54616 section.disclosureExpanded(null);
54622 section.tags = function (val) {
54623 if (!arguments.length) return _tags;
54628 section.entityIDs = function (val) {
54629 if (!arguments.length) return _entityIDs;
54631 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
54637 }; // pass an array of regular expressions to test against the tag key
54640 section.readOnlyTags = function (val) {
54641 if (!arguments.length) return _readOnlyTags;
54642 _readOnlyTags = val;
54646 return utilRebind(section, dispatch$1, 'on');
54649 function uiDataEditor(context) {
54650 var dataHeader = uiDataHeader();
54651 var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context).expandedByDefault(true).readOnlyTags([/./]);
54655 function dataEditor(selection) {
54656 var header = selection.selectAll('.header').data([0]);
54657 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
54658 headerEnter.append('button').attr('class', 'close').on('click', function () {
54659 context.enter(modeBrowse(context));
54660 }).call(svgIcon('#iD-icon-close'));
54661 headerEnter.append('h3').html(_t.html('map_data.title'));
54662 var body = selection.selectAll('.body').data([0]);
54663 body = body.enter().append('div').attr('class', 'body').merge(body);
54664 var editor = body.selectAll('.data-editor').data([0]); // enter/update
54666 editor.enter().append('div').attr('class', 'modal-section data-editor').merge(editor).call(dataHeader.datum(_datum));
54667 var rte = body.selectAll('.raw-tag-editor').data([0]); // enter/update
54669 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);
54672 dataEditor.datum = function (val) {
54673 if (!arguments.length) return _datum;
54681 function modeSelectData(context, selectedDatum) {
54686 var keybinding = utilKeybinding('select-data');
54687 var dataEditor = uiDataEditor(context);
54688 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
54690 function selectData(d3_event, drawn) {
54691 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
54693 if (selection.empty()) {
54694 // Return to browse mode if selected DOM elements have
54695 // disappeared because the user moved them out of view..
54696 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
54698 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
54699 context.enter(modeBrowse(context));
54702 selection.classed('selected', true);
54707 if (context.container().select('.combobox').size()) return;
54708 context.enter(modeBrowse(context));
54711 mode.zoomToSelected = function () {
54712 var extent = geoExtent(d3_geoBounds(selectedDatum));
54713 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
54716 mode.enter = function () {
54717 behaviors.forEach(context.install);
54718 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
54719 select(document).call(keybinding);
54721 var sidebar = context.ui().sidebar;
54722 sidebar.show(dataEditor.datum(selectedDatum)); // expand the sidebar, avoid obscuring the data if needed
54724 var extent = geoExtent(d3_geoBounds(selectedDatum));
54725 sidebar.expand(sidebar.intersects(extent));
54726 context.map().on('drawn.select-data', selectData);
54729 mode.exit = function () {
54730 behaviors.forEach(context.uninstall);
54731 select(document).call(keybinding.unbind);
54732 context.surface().selectAll('.layer-mapdata .selected').classed('selected hover', false);
54733 context.map().on('drawn.select-data', null);
54734 context.ui().sidebar.hide();
54740 function uiImproveOsmComments() {
54743 function issueComments(selection) {
54744 // make the div immediately so it appears above the buttons
54745 var comments = selection.selectAll('.comments-container').data([0]);
54746 comments = comments.enter().append('div').attr('class', 'comments-container').merge(comments); // must retrieve comments from API before they can be displayed
54748 services.improveOSM.getComments(_qaItem).then(function (d) {
54749 if (!d.comments) return; // nothing to do here
54751 var commentEnter = comments.selectAll('.comment').data(d.comments).enter().append('div').attr('class', 'comment');
54752 commentEnter.append('div').attr('class', 'comment-avatar').call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
54753 var mainEnter = commentEnter.append('div').attr('class', 'comment-main');
54754 var metadataEnter = mainEnter.append('div').attr('class', 'comment-metadata');
54755 metadataEnter.append('div').attr('class', 'comment-author').each(function (d) {
54756 var osm = services.osm;
54757 var selection = select(this);
54759 if (osm && d.username) {
54760 selection = selection.append('a').attr('class', 'comment-author-link').attr('href', osm.userURL(d.username)).attr('target', '_blank');
54763 selection.html(function (d) {
54767 metadataEnter.append('div').attr('class', 'comment-date').html(function (d) {
54768 return _t.html('note.status.commented', {
54769 when: localeDateString(d.timestamp)
54772 mainEnter.append('div').attr('class', 'comment-text').append('p').html(function (d) {
54775 })["catch"](function (err) {
54776 console.log(err); // eslint-disable-line no-console
54780 function localeDateString(s) {
54781 if (!s) return null;
54787 var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
54789 if (isNaN(d.getTime())) return null;
54790 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
54793 issueComments.issue = function (val) {
54794 if (!arguments.length) return _qaItem;
54796 return issueComments;
54799 return issueComments;
54802 function uiImproveOsmDetails(context) {
54805 function issueDetail(d) {
54806 if (d.desc) return d.desc;
54807 var issueKey = d.issueKey;
54808 d.replacements = d.replacements || {};
54809 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
54811 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".description"), d.replacements);
54814 function improveOsmDetails(selection) {
54815 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
54816 return "".concat(d.id, "-").concat(d.status || 0);
54818 details.exit().remove();
54819 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
54821 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
54822 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
54823 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
54825 var relatedEntities = [];
54826 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
54827 var link = select(this);
54828 var isObjectLink = link.classed('error_object_link');
54829 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
54830 var entity = context.hasEntity(entityID);
54831 relatedEntities.push(entityID); // Add click handler
54833 link.on('mouseenter', function () {
54834 utilHighlightEntities([entityID], true, context);
54835 }).on('mouseleave', function () {
54836 utilHighlightEntities([entityID], false, context);
54837 }).on('click', function (d3_event) {
54838 d3_event.preventDefault();
54839 utilHighlightEntities([entityID], false, context);
54840 var osmlayer = context.layers().layer('osm');
54842 if (!osmlayer.enabled()) {
54843 osmlayer.enabled(true);
54846 context.map().centerZoom(_qaItem.loc, 20);
54849 context.enter(modeSelect(context, [entityID]));
54851 context.loadEntity(entityID, function () {
54852 context.enter(modeSelect(context, [entityID]));
54855 }); // Replace with friendly name if possible
54856 // (The entity may not yet be loaded into the graph)
54859 var name = utilDisplayName(entity); // try to use common name
54861 if (!name && !isObjectLink) {
54862 var preset = _mainPresetIndex.match(entity, context.graph());
54863 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
54867 this.innerText = name;
54870 }); // Don't hide entities related to this error - #5880
54872 context.features().forceVisible(relatedEntities);
54873 context.map().pan([0, 0]); // trigger a redraw
54876 improveOsmDetails.issue = function (val) {
54877 if (!arguments.length) return _qaItem;
54879 return improveOsmDetails;
54882 return improveOsmDetails;
54885 function uiImproveOsmHeader() {
54888 function issueTitle(d) {
54889 var issueKey = d.issueKey;
54890 d.replacements = d.replacements || {};
54891 d.replacements["default"] = _t.html('inspector.unknown'); // special key `default` works as a fallback string
54893 return _t.html("QA.improveOSM.error_types.".concat(issueKey, ".title"), d.replacements);
54896 function improveOsmHeader(selection) {
54897 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
54898 return "".concat(d.id, "-").concat(d.status || 0);
54900 header.exit().remove();
54901 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
54902 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
54904 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
54905 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
54907 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');
54908 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
54909 var picon = d.icon;
54914 var isMaki = /^maki-/.test(picon);
54915 return "#".concat(picon).concat(isMaki ? '-11' : '');
54918 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
54921 improveOsmHeader.issue = function (val) {
54922 if (!arguments.length) return _qaItem;
54924 return improveOsmHeader;
54927 return improveOsmHeader;
54930 function uiImproveOsmEditor(context) {
54931 var dispatch$1 = dispatch('change');
54932 var qaDetails = uiImproveOsmDetails(context);
54933 var qaComments = uiImproveOsmComments();
54934 var qaHeader = uiImproveOsmHeader();
54938 function improveOsmEditor(selection) {
54939 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
54940 headerEnter.append('button').attr('class', 'close').on('click', function () {
54941 return context.enter(modeBrowse(context));
54942 }).call(svgIcon('#iD-icon-close'));
54943 headerEnter.append('h3').html(_t.html('QA.improveOSM.title'));
54944 var body = selection.selectAll('.body').data([0]);
54945 body = body.enter().append('div').attr('class', 'body').merge(body);
54946 var editor = body.selectAll('.qa-editor').data([0]);
54947 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);
54950 function improveOsmSaveSection(selection) {
54951 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54953 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
54954 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
54955 return "".concat(d.id, "-").concat(d.status || 0);
54958 saveSection.exit().remove(); // enter
54960 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
54961 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('note.newComment'));
54962 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
54963 return d.newComment;
54964 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
54966 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
54968 function changeInput() {
54969 var input = select(this);
54970 var val = input.property('value').trim();
54974 } // store the unsaved comment with the issue itself
54977 _qaItem = _qaItem.update({
54980 var qaService = services.improveOSM;
54983 qaService.replaceItem(_qaItem);
54986 saveSection.call(qaSaveButtons);
54990 function qaSaveButtons(selection) {
54991 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
54993 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
54994 return d.status + d.id;
54997 buttonSection.exit().remove(); // enter
54999 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
55000 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
55001 buttonEnter.append('button').attr('class', 'button close-button action');
55002 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
55004 buttonSection = buttonSection.merge(buttonEnter);
55005 buttonSection.select('.comment-button').attr('disabled', function (d) {
55006 return d.newComment ? null : true;
55007 }).on('click.comment', function (d3_event, d) {
55008 this.blur(); // avoid keeping focus on the button - #4641
55010 var qaService = services.improveOSM;
55013 qaService.postUpdate(d, function (err, item) {
55014 return dispatch$1.call('change', item);
55018 buttonSection.select('.close-button').html(function (d) {
55019 var andComment = d.newComment ? '_comment' : '';
55020 return _t.html("QA.keepRight.close".concat(andComment));
55021 }).on('click.close', function (d3_event, d) {
55022 this.blur(); // avoid keeping focus on the button - #4641
55024 var qaService = services.improveOSM;
55027 d.newStatus = 'SOLVED';
55028 qaService.postUpdate(d, function (err, item) {
55029 return dispatch$1.call('change', item);
55033 buttonSection.select('.ignore-button').html(function (d) {
55034 var andComment = d.newComment ? '_comment' : '';
55035 return _t.html("QA.keepRight.ignore".concat(andComment));
55036 }).on('click.ignore', function (d3_event, d) {
55037 this.blur(); // avoid keeping focus on the button - #4641
55039 var qaService = services.improveOSM;
55042 d.newStatus = 'INVALID';
55043 qaService.postUpdate(d, function (err, item) {
55044 return dispatch$1.call('change', item);
55048 } // NOTE: Don't change method name until UI v3 is merged
55051 improveOsmEditor.error = function (val) {
55052 if (!arguments.length) return _qaItem;
55054 return improveOsmEditor;
55057 return utilRebind(improveOsmEditor, dispatch$1, 'on');
55060 function uiKeepRightDetails(context) {
55063 function issueDetail(d) {
55064 var itemType = d.itemType,
55065 parentIssueType = d.parentIssueType;
55066 var unknown = _t.html('inspector.unknown');
55067 var replacements = d.replacements || {};
55068 replacements["default"] = unknown; // special key `default` works as a fallback string
55070 var detail = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".description"), replacements);
55072 if (detail === unknown) {
55073 detail = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".description"), replacements);
55079 function keepRightDetails(selection) {
55080 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
55081 return "".concat(d.id, "-").concat(d.status || 0);
55083 details.exit().remove();
55084 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // description
55086 var descriptionEnter = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55087 descriptionEnter.append('h4').html(_t.html('QA.keepRight.detail_description'));
55088 descriptionEnter.append('div').attr('class', 'qa-details-description-text').html(issueDetail); // If there are entity links in the error message..
55090 var relatedEntities = [];
55091 descriptionEnter.selectAll('.error_entity_link, .error_object_link').attr('href', '#').each(function () {
55092 var link = select(this);
55093 var isObjectLink = link.classed('error_object_link');
55094 var entityID = isObjectLink ? utilEntityRoot(_qaItem.objectType) + _qaItem.objectId : this.textContent;
55095 var entity = context.hasEntity(entityID);
55096 relatedEntities.push(entityID); // Add click handler
55098 link.on('mouseenter', function () {
55099 utilHighlightEntities([entityID], true, context);
55100 }).on('mouseleave', function () {
55101 utilHighlightEntities([entityID], false, context);
55102 }).on('click', function (d3_event) {
55103 d3_event.preventDefault();
55104 utilHighlightEntities([entityID], false, context);
55105 var osmlayer = context.layers().layer('osm');
55107 if (!osmlayer.enabled()) {
55108 osmlayer.enabled(true);
55111 context.map().centerZoomEase(_qaItem.loc, 20);
55114 context.enter(modeSelect(context, [entityID]));
55116 context.loadEntity(entityID, function () {
55117 context.enter(modeSelect(context, [entityID]));
55120 }); // Replace with friendly name if possible
55121 // (The entity may not yet be loaded into the graph)
55124 var name = utilDisplayName(entity); // try to use common name
55126 if (!name && !isObjectLink) {
55127 var preset = _mainPresetIndex.match(entity, context.graph());
55128 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
55132 this.innerText = name;
55135 }); // Don't hide entities related to this issue - #5880
55137 context.features().forceVisible(relatedEntities);
55138 context.map().pan([0, 0]); // trigger a redraw
55141 keepRightDetails.issue = function (val) {
55142 if (!arguments.length) return _qaItem;
55144 return keepRightDetails;
55147 return keepRightDetails;
55150 function uiKeepRightHeader() {
55153 function issueTitle(d) {
55154 var itemType = d.itemType,
55155 parentIssueType = d.parentIssueType;
55156 var unknown = _t.html('inspector.unknown');
55157 var replacements = d.replacements || {};
55158 replacements["default"] = unknown; // special key `default` works as a fallback string
55160 var title = _t.html("QA.keepRight.errorTypes.".concat(itemType, ".title"), replacements);
55162 if (title === unknown) {
55163 title = _t.html("QA.keepRight.errorTypes.".concat(parentIssueType, ".title"), replacements);
55169 function keepRightHeader(selection) {
55170 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
55171 return "".concat(d.id, "-").concat(d.status || 0);
55173 header.exit().remove();
55174 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
55175 var iconEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
55178 iconEnter.append('div').attr('class', function (d) {
55179 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
55180 }).call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
55181 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
55184 keepRightHeader.issue = function (val) {
55185 if (!arguments.length) return _qaItem;
55187 return keepRightHeader;
55190 return keepRightHeader;
55193 function uiViewOnKeepRight() {
55196 function viewOnKeepRight(selection) {
55199 if (services.keepRight && _qaItem instanceof QAItem) {
55200 url = services.keepRight.issueURL(_qaItem);
55203 var link = selection.selectAll('.view-on-keepRight').data(url ? [url] : []); // exit
55205 link.exit().remove(); // enter
55207 var linkEnter = link.enter().append('a').attr('class', 'view-on-keepRight').attr('target', '_blank').attr('rel', 'noopener') // security measure
55208 .attr('href', function (d) {
55210 }).call(svgIcon('#iD-icon-out-link', 'inline'));
55211 linkEnter.append('span').html(_t.html('inspector.view_on_keepRight'));
55214 viewOnKeepRight.what = function (val) {
55215 if (!arguments.length) return _qaItem;
55217 return viewOnKeepRight;
55220 return viewOnKeepRight;
55223 function uiKeepRightEditor(context) {
55224 var dispatch$1 = dispatch('change');
55225 var qaDetails = uiKeepRightDetails(context);
55226 var qaHeader = uiKeepRightHeader();
55230 function keepRightEditor(selection) {
55231 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
55232 headerEnter.append('button').attr('class', 'close').on('click', function () {
55233 return context.enter(modeBrowse(context));
55234 }).call(svgIcon('#iD-icon-close'));
55235 headerEnter.append('h3').html(_t.html('QA.keepRight.title'));
55236 var body = selection.selectAll('.body').data([0]);
55237 body = body.enter().append('div').attr('class', 'body').merge(body);
55238 var editor = body.selectAll('.qa-editor').data([0]);
55239 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(keepRightSaveSection);
55240 var footer = selection.selectAll('.footer').data([0]);
55241 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnKeepRight().what(_qaItem));
55244 function keepRightSaveSection(selection) {
55245 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55247 var isShown = _qaItem && (isSelected || _qaItem.newComment || _qaItem.comment);
55248 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
55249 return "".concat(d.id, "-").concat(d.status || 0);
55252 saveSection.exit().remove(); // enter
55254 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf');
55255 saveSectionEnter.append('h4').attr('class', '.qa-save-header').html(_t.html('QA.keepRight.comment'));
55256 saveSectionEnter.append('textarea').attr('class', 'new-comment-input').attr('placeholder', _t('QA.keepRight.comment_placeholder')).attr('maxlength', 1000).property('value', function (d) {
55257 return d.newComment || d.comment;
55258 }).call(utilNoAuto).on('input', changeInput).on('blur', changeInput); // update
55260 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
55262 function changeInput() {
55263 var input = select(this);
55264 var val = input.property('value').trim();
55266 if (val === _qaItem.comment) {
55268 } // store the unsaved comment with the issue itself
55271 _qaItem = _qaItem.update({
55274 var qaService = services.keepRight;
55277 qaService.replaceItem(_qaItem); // update keepright cache
55280 saveSection.call(qaSaveButtons);
55284 function qaSaveButtons(selection) {
55285 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55287 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
55288 return d.status + d.id;
55291 buttonSection.exit().remove(); // enter
55293 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
55294 buttonEnter.append('button').attr('class', 'button comment-button action').html(_t.html('QA.keepRight.save_comment'));
55295 buttonEnter.append('button').attr('class', 'button close-button action');
55296 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
55298 buttonSection = buttonSection.merge(buttonEnter);
55299 buttonSection.select('.comment-button') // select and propagate data
55300 .attr('disabled', function (d) {
55301 return d.newComment ? null : true;
55302 }).on('click.comment', function (d3_event, d) {
55303 this.blur(); // avoid keeping focus on the button - #4641
55305 var qaService = services.keepRight;
55308 qaService.postUpdate(d, function (err, item) {
55309 return dispatch$1.call('change', item);
55313 buttonSection.select('.close-button') // select and propagate data
55314 .html(function (d) {
55315 var andComment = d.newComment ? '_comment' : '';
55316 return _t.html("QA.keepRight.close".concat(andComment));
55317 }).on('click.close', function (d3_event, d) {
55318 this.blur(); // avoid keeping focus on the button - #4641
55320 var qaService = services.keepRight;
55323 d.newStatus = 'ignore_t'; // ignore temporarily (item fixed)
55325 qaService.postUpdate(d, function (err, item) {
55326 return dispatch$1.call('change', item);
55330 buttonSection.select('.ignore-button') // select and propagate data
55331 .html(function (d) {
55332 var andComment = d.newComment ? '_comment' : '';
55333 return _t.html("QA.keepRight.ignore".concat(andComment));
55334 }).on('click.ignore', function (d3_event, d) {
55335 this.blur(); // avoid keeping focus on the button - #4641
55337 var qaService = services.keepRight;
55340 d.newStatus = 'ignore'; // ignore permanently (false positive)
55342 qaService.postUpdate(d, function (err, item) {
55343 return dispatch$1.call('change', item);
55347 } // NOTE: Don't change method name until UI v3 is merged
55350 keepRightEditor.error = function (val) {
55351 if (!arguments.length) return _qaItem;
55353 return keepRightEditor;
55356 return utilRebind(keepRightEditor, dispatch$1, 'on');
55359 function uiOsmoseDetails(context) {
55362 function issueString(d, type) {
55363 if (!d) return ''; // Issue strings are cached from Osmose API
55365 var s = services.osmose.getStrings(d.itemType);
55366 return type in s ? s[type] : '';
55369 function osmoseDetails(selection) {
55370 var details = selection.selectAll('.error-details').data(_qaItem ? [_qaItem] : [], function (d) {
55371 return "".concat(d.id, "-").concat(d.status || 0);
55373 details.exit().remove();
55374 var detailsEnter = details.enter().append('div').attr('class', 'error-details qa-details-container'); // Description
55376 if (issueString(_qaItem, 'detail')) {
55377 var div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55378 div.append('h4').html(_t.html('QA.keepRight.detail_description'));
55379 div.append('p').attr('class', 'qa-details-description-text').html(function (d) {
55380 return issueString(d, 'detail');
55381 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55382 } // Elements (populated later as data is requested)
55385 var detailsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55386 var elemsDiv = detailsEnter.append('div').attr('class', 'qa-details-subsection'); // Suggested Fix (mustn't exist for every issue type)
55388 if (issueString(_qaItem, 'fix')) {
55389 var _div = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55391 _div.append('h4').html(_t.html('QA.osmose.fix_title'));
55393 _div.append('p').html(function (d) {
55394 return issueString(d, 'fix');
55395 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55396 } // Common Pitfalls (mustn't exist for every issue type)
55399 if (issueString(_qaItem, 'trap')) {
55400 var _div2 = detailsEnter.append('div').attr('class', 'qa-details-subsection');
55402 _div2.append('h4').html(_t.html('QA.osmose.trap_title'));
55404 _div2.append('p').html(function (d) {
55405 return issueString(d, 'trap');
55406 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55407 } // Save current item to check if UI changed by time request resolves
55410 var thisItem = _qaItem;
55411 services.osmose.loadIssueDetail(_qaItem).then(function (d) {
55412 // No details to add if there are no associated issue elements
55413 if (!d.elems || d.elems.length === 0) return; // Do nothing if UI has moved on by the time this resolves
55415 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
55418 detailsDiv.append('h4').html(_t.html('QA.osmose.detail_title'));
55419 detailsDiv.append('p').html(function (d) {
55421 }).selectAll('a').attr('rel', 'noopener').attr('target', '_blank');
55422 } // Create list of linked issue elements
55425 elemsDiv.append('h4').html(_t.html('QA.osmose.elems_title'));
55426 elemsDiv.append('ul').selectAll('li').data(d.elems).enter().append('li').append('a').attr('href', '#').attr('class', 'error_entity_link').html(function (d) {
55428 }).each(function () {
55429 var link = select(this);
55430 var entityID = this.textContent;
55431 var entity = context.hasEntity(entityID); // Add click handler
55433 link.on('mouseenter', function () {
55434 utilHighlightEntities([entityID], true, context);
55435 }).on('mouseleave', function () {
55436 utilHighlightEntities([entityID], false, context);
55437 }).on('click', function (d3_event) {
55438 d3_event.preventDefault();
55439 utilHighlightEntities([entityID], false, context);
55440 var osmlayer = context.layers().layer('osm');
55442 if (!osmlayer.enabled()) {
55443 osmlayer.enabled(true);
55446 context.map().centerZoom(d.loc, 20);
55449 context.enter(modeSelect(context, [entityID]));
55451 context.loadEntity(entityID, function () {
55452 context.enter(modeSelect(context, [entityID]));
55455 }); // Replace with friendly name if possible
55456 // (The entity may not yet be loaded into the graph)
55459 var name = utilDisplayName(entity); // try to use common name
55462 var preset = _mainPresetIndex.match(entity, context.graph());
55463 name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
55467 this.innerText = name;
55470 }); // Don't hide entities related to this issue - #5880
55472 context.features().forceVisible(d.elems);
55473 context.map().pan([0, 0]); // trigger a redraw
55474 })["catch"](function (err) {
55475 console.log(err); // eslint-disable-line no-console
55479 osmoseDetails.issue = function (val) {
55480 if (!arguments.length) return _qaItem;
55482 return osmoseDetails;
55485 return osmoseDetails;
55488 function uiOsmoseHeader() {
55491 function issueTitle(d) {
55492 var unknown = _t('inspector.unknown');
55493 if (!d) return unknown; // Issue titles supplied by Osmose
55495 var s = services.osmose.getStrings(d.itemType);
55496 return 'title' in s ? s.title : unknown;
55499 function osmoseHeader(selection) {
55500 var header = selection.selectAll('.qa-header').data(_qaItem ? [_qaItem] : [], function (d) {
55501 return "".concat(d.id, "-").concat(d.status || 0);
55503 header.exit().remove();
55504 var headerEnter = header.enter().append('div').attr('class', 'qa-header');
55505 var svgEnter = headerEnter.append('div').attr('class', 'qa-header-icon').classed('new', function (d) {
55507 }).append('svg').attr('width', '20px').attr('height', '30px').attr('viewbox', '0 0 20 30').attr('class', function (d) {
55508 return "preset-icon-28 qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
55510 svgEnter.append('polygon').attr('fill', function (d) {
55511 return services.osmose.getColor(d.item);
55512 }).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');
55513 svgEnter.append('use').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('transform', 'translate(3.5, 5)').attr('xlink:href', function (d) {
55514 var picon = d.icon;
55519 var isMaki = /^maki-/.test(picon);
55520 return "#".concat(picon).concat(isMaki ? '-11' : '');
55523 headerEnter.append('div').attr('class', 'qa-header-label').html(issueTitle);
55526 osmoseHeader.issue = function (val) {
55527 if (!arguments.length) return _qaItem;
55529 return osmoseHeader;
55532 return osmoseHeader;
55535 function uiViewOnOsmose() {
55538 function viewOnOsmose(selection) {
55541 if (services.osmose && _qaItem instanceof QAItem) {
55542 url = services.osmose.itemURL(_qaItem);
55545 var link = selection.selectAll('.view-on-osmose').data(url ? [url] : []); // exit
55547 link.exit().remove(); // enter
55549 var linkEnter = link.enter().append('a').attr('class', 'view-on-osmose').attr('target', '_blank').attr('rel', 'noopener') // security measure
55550 .attr('href', function (d) {
55552 }).call(svgIcon('#iD-icon-out-link', 'inline'));
55553 linkEnter.append('span').html(_t.html('inspector.view_on_osmose'));
55556 viewOnOsmose.what = function (val) {
55557 if (!arguments.length) return _qaItem;
55559 return viewOnOsmose;
55562 return viewOnOsmose;
55565 function uiOsmoseEditor(context) {
55566 var dispatch$1 = dispatch('change');
55567 var qaDetails = uiOsmoseDetails(context);
55568 var qaHeader = uiOsmoseHeader();
55572 function osmoseEditor(selection) {
55573 var header = selection.selectAll('.header').data([0]);
55574 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
55575 headerEnter.append('button').attr('class', 'close').on('click', function () {
55576 return context.enter(modeBrowse(context));
55577 }).call(svgIcon('#iD-icon-close'));
55578 headerEnter.append('h3').html(_t.html('QA.osmose.title'));
55579 var body = selection.selectAll('.body').data([0]);
55580 body = body.enter().append('div').attr('class', 'body').merge(body);
55581 var editor = body.selectAll('.qa-editor').data([0]);
55582 editor.enter().append('div').attr('class', 'modal-section qa-editor').merge(editor).call(qaHeader.issue(_qaItem)).call(qaDetails.issue(_qaItem)).call(osmoseSaveSection);
55583 var footer = selection.selectAll('.footer').data([0]);
55584 footer.enter().append('div').attr('class', 'footer').merge(footer).call(uiViewOnOsmose().what(_qaItem));
55587 function osmoseSaveSection(selection) {
55588 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55590 var isShown = _qaItem && isSelected;
55591 var saveSection = selection.selectAll('.qa-save').data(isShown ? [_qaItem] : [], function (d) {
55592 return "".concat(d.id, "-").concat(d.status || 0);
55595 saveSection.exit().remove(); // enter
55597 var saveSectionEnter = saveSection.enter().append('div').attr('class', 'qa-save save-section cf'); // update
55599 saveSection = saveSectionEnter.merge(saveSection).call(qaSaveButtons);
55602 function qaSaveButtons(selection) {
55603 var isSelected = _qaItem && _qaItem.id === context.selectedErrorID();
55605 var buttonSection = selection.selectAll('.buttons').data(isSelected ? [_qaItem] : [], function (d) {
55606 return d.status + d.id;
55609 buttonSection.exit().remove(); // enter
55611 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons');
55612 buttonEnter.append('button').attr('class', 'button close-button action');
55613 buttonEnter.append('button').attr('class', 'button ignore-button action'); // update
55615 buttonSection = buttonSection.merge(buttonEnter);
55616 buttonSection.select('.close-button').html(_t.html('QA.keepRight.close')).on('click.close', function (d3_event, d) {
55617 this.blur(); // avoid keeping focus on the button - #4641
55619 var qaService = services.osmose;
55622 d.newStatus = 'done';
55623 qaService.postUpdate(d, function (err, item) {
55624 return dispatch$1.call('change', item);
55628 buttonSection.select('.ignore-button').html(_t.html('QA.keepRight.ignore')).on('click.ignore', function (d3_event, d) {
55629 this.blur(); // avoid keeping focus on the button - #4641
55631 var qaService = services.osmose;
55634 d.newStatus = 'false';
55635 qaService.postUpdate(d, function (err, item) {
55636 return dispatch$1.call('change', item);
55640 } // NOTE: Don't change method name until UI v3 is merged
55643 osmoseEditor.error = function (val) {
55644 if (!arguments.length) return _qaItem;
55646 return osmoseEditor;
55649 return utilRebind(osmoseEditor, dispatch$1, 'on');
55652 function modeSelectError(context, selectedErrorID, selectedErrorService) {
55654 id: 'select-error',
55657 var keybinding = utilKeybinding('select-error');
55658 var errorService = services[selectedErrorService];
55661 switch (selectedErrorService) {
55663 errorEditor = uiImproveOsmEditor(context).on('change', function () {
55664 context.map().pan([0, 0]); // trigger a redraw
55666 var error = checkSelectedID();
55667 if (!error) return;
55668 context.ui().sidebar.show(errorEditor.error(error));
55673 errorEditor = uiKeepRightEditor(context).on('change', function () {
55674 context.map().pan([0, 0]); // trigger a redraw
55676 var error = checkSelectedID();
55677 if (!error) return;
55678 context.ui().sidebar.show(errorEditor.error(error));
55683 errorEditor = uiOsmoseEditor(context).on('change', function () {
55684 context.map().pan([0, 0]); // trigger a redraw
55686 var error = checkSelectedID();
55687 if (!error) return;
55688 context.ui().sidebar.show(errorEditor.error(error));
55693 var behaviors = [behaviorBreathe(), behaviorHover(context), behaviorSelect(context), behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
55695 function checkSelectedID() {
55696 if (!errorService) return;
55697 var error = errorService.getError(selectedErrorID);
55700 context.enter(modeBrowse(context));
55706 mode.zoomToSelected = function () {
55707 if (!errorService) return;
55708 var error = errorService.getError(selectedErrorID);
55711 context.map().centerZoomEase(error.loc, 20);
55715 mode.enter = function () {
55716 var error = checkSelectedID();
55717 if (!error) return;
55718 behaviors.forEach(context.install);
55719 keybinding.on(_t('inspector.zoom_to.key'), mode.zoomToSelected).on('⎋', esc, true);
55720 select(document).call(keybinding);
55722 var sidebar = context.ui().sidebar;
55723 sidebar.show(errorEditor.error(error));
55724 context.map().on('drawn.select-error', selectError); // class the error as selected, or return to browse mode if the error is gone
55726 function selectError(d3_event, drawn) {
55727 if (!checkSelectedID()) return;
55728 var selection = context.surface().selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
55730 if (selection.empty()) {
55731 // Return to browse mode if selected DOM elements have
55732 // disappeared because the user moved them out of view..
55733 var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
55735 if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
55736 context.enter(modeBrowse(context));
55739 selection.classed('selected', true);
55740 context.selectedErrorID(selectedErrorID);
55745 if (context.container().select('.combobox').size()) return;
55746 context.enter(modeBrowse(context));
55750 mode.exit = function () {
55751 behaviors.forEach(context.uninstall);
55752 select(document).call(keybinding.unbind);
55753 context.surface().selectAll('.qaItem.selected').classed('selected hover', false);
55754 context.map().on('drawn.select-error', null);
55755 context.ui().sidebar.hide();
55756 context.selectedErrorID(null);
55757 context.features().forceVisible([]);
55763 function behaviorSelect(context) {
55764 var _tolerancePx = 4; // see also behaviorDrag
55766 var _lastMouseEvent = null;
55767 var _showMenu = false;
55768 var _downPointers = {};
55769 var _longPressTimeout = null;
55770 var _lastInteractionType = null; // the id of the down pointer that's enabling multiselection while down
55772 var _multiselectionPointerId = null; // use pointer events on supported platforms; fallback to mouse events
55774 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55776 function keydown(d3_event) {
55777 if (d3_event.keyCode === 32) {
55778 // don't react to spacebar events during text input
55779 var activeNode = document.activeElement;
55780 if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) return;
55783 if (d3_event.keyCode === 93 || // context menu key
55784 d3_event.keyCode === 32) {
55786 d3_event.preventDefault();
55789 if (d3_event.repeat) return; // ignore repeated events for held keys
55790 // if any key is pressed the user is probably doing something other than long-pressing
55794 if (d3_event.shiftKey) {
55795 context.surface().classed('behavior-multiselect', true);
55798 if (d3_event.keyCode === 32) {
55800 if (!_downPointers.spacebar && _lastMouseEvent) {
55802 _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
55803 _downPointers.spacebar = {
55804 firstEvent: _lastMouseEvent,
55805 lastEvent: _lastMouseEvent
55811 function keyup(d3_event) {
55814 if (!d3_event.shiftKey) {
55815 context.surface().classed('behavior-multiselect', false);
55818 if (d3_event.keyCode === 93) {
55819 // context menu key
55820 d3_event.preventDefault();
55821 _lastInteractionType = 'menukey';
55822 contextmenu(d3_event);
55823 } else if (d3_event.keyCode === 32) {
55825 var pointer = _downPointers.spacebar;
55828 delete _downPointers.spacebar;
55829 if (pointer.done) return;
55830 d3_event.preventDefault();
55831 _lastInteractionType = 'spacebar';
55832 click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
55837 function pointerdown(d3_event) {
55838 var id = (d3_event.pointerId || 'mouse').toString();
55840 if (d3_event.buttons && d3_event.buttons !== 1) return;
55841 context.ui().closeEditMenu();
55842 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (d3_event.pointerType || 'mouse'));
55843 _downPointers[id] = {
55844 firstEvent: d3_event,
55845 lastEvent: d3_event
55849 function didLongPress(id, interactionType) {
55850 var pointer = _downPointers[id];
55851 if (!pointer) return;
55853 for (var i in _downPointers) {
55854 // don't allow this or any currently down pointer to trigger another click
55855 _downPointers[i].done = true;
55856 } // treat long presses like right-clicks
55859 _longPressTimeout = null;
55860 _lastInteractionType = interactionType;
55862 click(pointer.firstEvent, pointer.lastEvent, id);
55865 function pointermove(d3_event) {
55866 var id = (d3_event.pointerId || 'mouse').toString();
55868 if (_downPointers[id]) {
55869 _downPointers[id].lastEvent = d3_event;
55872 if (!d3_event.pointerType || d3_event.pointerType === 'mouse') {
55873 _lastMouseEvent = d3_event;
55875 if (_downPointers.spacebar) {
55876 _downPointers.spacebar.lastEvent = d3_event;
55881 function pointerup(d3_event) {
55882 var id = (d3_event.pointerId || 'mouse').toString();
55883 var pointer = _downPointers[id];
55884 if (!pointer) return;
55885 delete _downPointers[id];
55887 if (_multiselectionPointerId === id) {
55888 _multiselectionPointerId = null;
55891 if (pointer.done) return;
55892 click(pointer.firstEvent, d3_event, id);
55895 function pointercancel(d3_event) {
55896 var id = (d3_event.pointerId || 'mouse').toString();
55897 if (!_downPointers[id]) return;
55898 delete _downPointers[id];
55900 if (_multiselectionPointerId === id) {
55901 _multiselectionPointerId = null;
55905 function contextmenu(d3_event) {
55906 d3_event.preventDefault();
55908 if (!+d3_event.clientX && !+d3_event.clientY) {
55909 if (_lastMouseEvent) {
55910 d3_event.sourceEvent = _lastMouseEvent;
55915 _lastMouseEvent = d3_event;
55916 _lastInteractionType = 'rightclick';
55920 click(d3_event, d3_event);
55923 function click(firstEvent, lastEvent, pointerId) {
55925 var mapNode = context.container().select('.main-map').node(); // Use the `main-map` coordinate system since the surface and supersurface
55926 // are transformed when drag-panning.
55928 var pointGetter = utilFastMouse(mapNode);
55929 var p1 = pointGetter(firstEvent);
55930 var p2 = pointGetter(lastEvent);
55931 var dist = geoVecLength(p1, p2);
55933 if (dist > _tolerancePx || !mapContains(lastEvent)) {
55938 var targetDatum = lastEvent.target.__data__;
55939 var multiselectEntityId;
55941 if (!_multiselectionPointerId) {
55942 // If a different pointer than the one triggering this click is down on a
55943 // feature, treat this and all future clicks as multiselection until that
55944 // pointer is raised.
55945 var selectPointerInfo = pointerDownOnSelection(pointerId);
55947 if (selectPointerInfo) {
55948 _multiselectionPointerId = selectPointerInfo.pointerId; // if the other feature isn't selected yet, make sure we select it
55950 multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
55951 _downPointers[selectPointerInfo.pointerId].done = true;
55953 } // support multiselect if data is already selected
55956 var isMultiselect = context.mode().id === 'select' && ( // and shift key is down
55957 lastEvent && lastEvent.shiftKey || // or we're lasso-selecting
55958 context.surface().select('.lasso').node() || // or a pointer is down over a selected feature
55959 _multiselectionPointerId && !multiselectEntityId);
55961 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
55963 function mapContains(event) {
55964 var rect = mapNode.getBoundingClientRect();
55965 return event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;
55968 function pointerDownOnSelection(skipPointerId) {
55969 var mode = context.mode();
55970 var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
55972 for (var pointerId in _downPointers) {
55973 if (pointerId === 'spacebar' || pointerId === skipPointerId) continue;
55974 var pointerInfo = _downPointers[pointerId];
55975 var p1 = pointGetter(pointerInfo.firstEvent);
55976 var p2 = pointGetter(pointerInfo.lastEvent);
55977 if (geoVecLength(p1, p2) > _tolerancePx) continue;
55978 var datum = pointerInfo.firstEvent.target.__data__;
55979 var entity = datum && datum.properties && datum.properties.entity || datum;
55980 if (context.graph().hasEntity(entity.id)) return {
55981 pointerId: pointerId,
55982 entityId: entity.id,
55983 selected: selectedIDs.indexOf(entity.id) !== -1
55991 function processClick(datum, isMultiselect, point, alsoSelectId) {
55992 var mode = context.mode();
55993 var showMenu = _showMenu;
55994 var interactionType = _lastInteractionType;
55995 var entity = datum && datum.properties && datum.properties.entity;
55996 if (entity) datum = entity;
55998 if (datum && datum.type === 'midpoint') {
55999 // treat targeting midpoints as if targeting the parent way
56000 datum = datum.parents[0];
56005 if (datum instanceof osmEntity) {
56006 // targeting an entity
56007 var selectedIDs = context.selectedIDs();
56008 context.selectedNoteID(null);
56009 context.selectedErrorID(null);
56011 if (!isMultiselect) {
56012 // don't change the selection if we're toggling the menu atop a multiselection
56013 if (!showMenu || selectedIDs.length <= 1 || selectedIDs.indexOf(datum.id) === -1) {
56014 if (alsoSelectId === datum.id) alsoSelectId = null;
56015 selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]); // always enter modeSelect even if the entity is already
56016 // selected since listeners may expect `context.enter` events,
56017 // e.g. in the walkthrough
56019 newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
56020 context.enter(newMode);
56023 if (selectedIDs.indexOf(datum.id) !== -1) {
56024 // clicked entity is already in the selectedIDs list..
56026 // deselect clicked entity, then reenter select mode or return to browse mode..
56027 selectedIDs = selectedIDs.filter(function (id) {
56028 return id !== datum.id;
56030 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
56031 context.enter(newMode);
56034 // clicked entity is not in the selected list, add it..
56035 selectedIDs = selectedIDs.concat([datum.id]);
56036 newMode = mode.selectedIDs(selectedIDs);
56037 context.enter(newMode);
56040 } else if (datum && datum.__featurehash__ && !isMultiselect) {
56041 // targeting custom data
56042 context.selectedNoteID(null).enter(modeSelectData(context, datum));
56043 } else if (datum instanceof osmNote && !isMultiselect) {
56044 // targeting a note
56045 context.selectedNoteID(datum.id).enter(modeSelectNote(context, datum.id));
56046 } else if (datum instanceof QAItem & !isMultiselect) {
56047 // targeting an external QA issue
56048 context.selectedErrorID(datum.id).enter(modeSelectError(context, datum.id, datum.service));
56050 // targeting nothing
56051 context.selectedNoteID(null);
56052 context.selectedErrorID(null);
56054 if (!isMultiselect && mode.id !== 'browse') {
56055 context.enter(modeBrowse(context));
56059 context.ui().closeEditMenu(); // always request to show the edit menu in case the mode needs it
56061 if (showMenu) context.ui().showEditMenu(point, interactionType);
56065 function cancelLongPress() {
56066 if (_longPressTimeout) window.clearTimeout(_longPressTimeout);
56067 _longPressTimeout = null;
56070 function resetProperties() {
56073 _lastInteractionType = null; // don't reset _lastMouseEvent since it might still be useful
56076 function behavior(selection) {
56078 _lastMouseEvent = context.map().lastPointerEvent();
56079 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) {
56080 // Edge and IE really like to show the contextmenu on the
56081 // menubar when user presses a keyboard menu button
56082 // even after we've already preventdefaulted the key event.
56085 if (+e.clientX === 0 && +e.clientY === 0) {
56086 d3_event.preventDefault();
56089 selection.on(_pointerPrefix + 'down.select', pointerdown).on('contextmenu.select', contextmenu);
56090 /*if (d3_event && d3_event.shiftKey) {
56092 .classed('behavior-multiselect', true);
56096 behavior.off = function (selection) {
56098 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);
56099 selection.on(_pointerPrefix + 'down.select', null).on('contextmenu.select', null);
56100 context.surface().classed('behavior-multiselect', false);
56106 function behaviorDrawWay(context, wayID, mode, startGraph) {
56107 var dispatch$1 = dispatch('rejectedSelfIntersection');
56108 var behavior = behaviorDraw(context); // Must be set by `drawWay.nodeIndex` before each install of this behavior.
56120 var _pointerHasMoved = false; // The osmNode to be placed.
56121 // This is temporary and just follows the mouse cursor until an "add" event occurs.
56125 var _didResolveTempEdit = false;
56127 function createDrawNode(loc) {
56128 // don't make the draw node until we actually need it
56129 _drawNode = osmNode({
56132 context.pauseChangeDispatch();
56133 context.replace(function actionAddDrawNode(graph) {
56134 // add the draw node to the graph and insert it into the way
56135 var way = graph.entity(wayID);
56136 return graph.replace(_drawNode).replace(way.addNode(_drawNode.id, _nodeIndex));
56138 context.resumeChangeDispatch();
56139 setActiveElements();
56142 function removeDrawNode() {
56143 context.pauseChangeDispatch();
56144 context.replace(function actionDeleteDrawNode(graph) {
56145 var way = graph.entity(wayID);
56146 return graph.replace(way.removeNode(_drawNode.id)).remove(_drawNode);
56148 _drawNode = undefined;
56149 context.resumeChangeDispatch();
56152 function keydown(d3_event) {
56153 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56154 if (context.surface().classed('nope')) {
56155 context.surface().classed('nope-suppressed', true);
56158 context.surface().classed('nope', false).classed('nope-disabled', true);
56162 function keyup(d3_event) {
56163 if (d3_event.keyCode === utilKeybinding.modifierCodes.alt) {
56164 if (context.surface().classed('nope-suppressed')) {
56165 context.surface().classed('nope', true);
56168 context.surface().classed('nope-suppressed', false).classed('nope-disabled', false);
56172 function allowsVertex(d) {
56173 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
56175 // - `mode/drag_node.js` `doMove()`
56176 // - `behavior/draw.js` `click()`
56177 // - `behavior/draw_way.js` `move()`
56180 function move(d3_event, datum) {
56181 var loc = context.map().mouseCoordinates();
56182 if (!_drawNode) createDrawNode(loc);
56183 context.surface().classed('nope-disabled', d3_event.altKey);
56184 var targetLoc = datum && datum.properties && datum.properties.entity && allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
56185 var targetNodes = datum && datum.properties && datum.properties.nodes;
56188 // snap to node/vertex - a point target with `.loc`
56190 } else if (targetNodes) {
56191 // snap to way - a line target with `.nodes`
56192 var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
56199 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56200 _drawNode = context.entity(_drawNode.id);
56202 /* includeDrawNode */
56204 } // Check whether this edit causes the geometry to break.
56205 // If so, class the surface with a nope cursor.
56206 // `includeDrawNode` - Only check the relevant line segments if finishing drawing
56209 function checkGeometry(includeDrawNode) {
56210 var nopeDisabled = context.surface().classed('nope-disabled');
56211 var isInvalid = isInvalidGeometry(includeDrawNode);
56213 if (nopeDisabled) {
56214 context.surface().classed('nope', false).classed('nope-suppressed', isInvalid);
56216 context.surface().classed('nope', isInvalid).classed('nope-suppressed', false);
56220 function isInvalidGeometry(includeDrawNode) {
56221 var testNode = _drawNode; // we only need to test the single way we're drawing
56223 var parentWay = context.graph().entity(wayID);
56224 var nodes = context.graph().childNodes(parentWay).slice(); // shallow copy
56226 if (includeDrawNode) {
56227 if (parentWay.isClosed()) {
56228 // don't test the last segment for closed ways - #4655
56229 // (still test the first segment)
56233 // discount the draw node
56234 if (parentWay.isClosed()) {
56235 if (nodes.length < 3) return false;
56236 if (_drawNode) nodes.splice(-2, 1);
56237 testNode = nodes[nodes.length - 2];
56239 // there's nothing we need to test if we ignore the draw node on open ways
56244 return testNode && geoHasSelfIntersections(nodes, testNode.id);
56247 function undone() {
56248 // undoing removed the temp edit
56249 _didResolveTempEdit = true;
56250 context.pauseChangeDispatch();
56253 if (context.graph() === startGraph) {
56254 // We've undone back to the initial state before we started drawing.
56255 // Just exit the draw mode without undoing whatever we did before
56256 // we entered the draw mode.
56257 nextMode = modeSelect(context, [wayID]);
56259 // The `undo` only removed the temporary edit, so here we have to
56260 // manually undo to actually remove the last node we added. We can't
56261 // use the `undo` function since the initial "add" graph doesn't have
56262 // an annotation and so cannot be undone to.
56263 context.pop(1); // continue drawing
56266 } // clear the redo stack by adding and removing a blank edit
56269 context.perform(actionNoop());
56271 context.resumeChangeDispatch();
56272 context.enter(nextMode);
56275 function setActiveElements() {
56276 if (!_drawNode) return;
56277 context.surface().selectAll('.' + _drawNode.id).classed('active', true);
56280 function resetToStartGraph() {
56281 while (context.graph() !== startGraph) {
56286 var drawWay = function drawWay(surface) {
56287 _drawNode = undefined;
56288 _didResolveTempEdit = false;
56289 _origWay = context.entity(wayID);
56290 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] : _origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1];
56291 _wayGeometry = _origWay.geometry(context.graph());
56292 _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ? 'operations.start.annotation.' : 'operations.continue.annotation.') + _wayGeometry);
56293 _pointerHasMoved = false; // Push an annotated state for undo to return back to.
56294 // We must make sure to replace or remove it later.
56296 context.pauseChangeDispatch();
56297 context.perform(actionNoop(), _annotation);
56298 context.resumeChangeDispatch();
56299 behavior.hover().initialNodeID(_headNodeID);
56300 behavior.on('move', function () {
56301 _pointerHasMoved = true;
56302 move.apply(this, arguments);
56303 }).on('down', function () {
56304 move.apply(this, arguments);
56305 }).on('downcancel', function () {
56306 if (_drawNode) removeDrawNode();
56307 }).on('click', drawWay.add).on('clickWay', drawWay.addWay).on('clickNode', drawWay.addNode).on('undo', context.undo).on('cancel', drawWay.cancel).on('finish', drawWay.finish);
56308 select(window).on('keydown.drawWay', keydown).on('keyup.drawWay', keyup);
56309 context.map().dblclickZoomEnable(false).on('drawn.draw', setActiveElements);
56310 setActiveElements();
56311 surface.call(behavior);
56312 context.history().on('undone.draw', undone);
56315 drawWay.off = function (surface) {
56316 if (!_didResolveTempEdit) {
56317 // Drawing was interrupted unexpectedly.
56318 // This can happen if the user changes modes,
56319 // clicks geolocate button, a hashchange event occurs, etc.
56320 context.pauseChangeDispatch();
56321 resetToStartGraph();
56322 context.resumeChangeDispatch();
56325 _drawNode = undefined;
56326 _nodeIndex = undefined;
56327 context.map().on('drawn.draw', null);
56328 surface.call(behavior.off).selectAll('.active').classed('active', false);
56329 surface.classed('nope', false).classed('nope-suppressed', false).classed('nope-disabled', false);
56330 select(window).on('keydown.drawWay', null).on('keyup.drawWay', null);
56331 context.history().on('undone.draw', null);
56334 function attemptAdd(d, loc, doAdd) {
56336 // move the node to the final loc in case move wasn't called
56337 // consistently (e.g. on touch devices)
56338 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
56339 _drawNode = context.entity(_drawNode.id);
56341 createDrawNode(loc);
56345 /* includeDrawNode */
56348 if (d && d.properties && d.properties.nope || context.surface().classed('nope')) {
56349 if (!_pointerHasMoved) {
56350 // prevent the temporary draw node from appearing on touch devices
56354 dispatch$1.call('rejectedSelfIntersection', this);
56355 return; // can't click here
56358 context.pauseChangeDispatch();
56359 doAdd(); // we just replaced the temporary edit with the real one
56361 _didResolveTempEdit = true;
56362 context.resumeChangeDispatch();
56363 context.enter(mode);
56364 } // Accept the current position of the drawing node
56367 drawWay.add = function (loc, d) {
56368 attemptAdd(d, loc, function () {// don't need to do anything extra
56370 }; // Connect the way to an existing way
56373 drawWay.addWay = function (loc, edge, d) {
56374 attemptAdd(d, loc, function () {
56375 context.replace(actionAddMidpoint({
56378 }, _drawNode), _annotation);
56380 }; // Connect the way to an existing node
56383 drawWay.addNode = function (node, d) {
56384 // finish drawing if the mapper targets the prior node
56385 if (node.id === _headNodeID || // or the first node when drawing an area
56386 _origWay.isClosed() && node.id === _origWay.first()) {
56391 attemptAdd(d, node.loc, function () {
56392 context.replace(function actionReplaceDrawNode(graph) {
56393 // remove the temporary draw node and insert the existing node
56394 // at the same index
56395 graph = graph.replace(graph.entity(wayID).removeNode(_drawNode.id)).remove(_drawNode);
56396 return graph.replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
56399 }; // Finish the draw operation, removing the temporary edit.
56400 // If the way has enough nodes to be valid, it's selected.
56401 // Otherwise, delete everything and return to browse mode.
56404 drawWay.finish = function () {
56405 checkGeometry(false
56406 /* includeDrawNode */
56409 if (context.surface().classed('nope')) {
56410 dispatch$1.call('rejectedSelfIntersection', this);
56411 return; // can't click here
56414 context.pauseChangeDispatch(); // remove the temporary edit
56417 _didResolveTempEdit = true;
56418 context.resumeChangeDispatch();
56419 var way = context.hasEntity(wayID);
56421 if (!way || way.isDegenerate()) {
56426 window.setTimeout(function () {
56427 context.map().dblclickZoomEnable(true);
56429 var isNewFeature = !mode.isContinuing;
56430 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
56431 }; // Cancel the draw operation, delete everything, and return to browse mode.
56434 drawWay.cancel = function () {
56435 context.pauseChangeDispatch();
56436 resetToStartGraph();
56437 context.resumeChangeDispatch();
56438 window.setTimeout(function () {
56439 context.map().dblclickZoomEnable(true);
56441 context.surface().classed('nope', false).classed('nope-disabled', false).classed('nope-suppressed', false);
56442 context.enter(modeBrowse(context));
56445 drawWay.nodeIndex = function (val) {
56446 if (!arguments.length) return _nodeIndex;
56451 drawWay.activeID = function () {
56452 if (!arguments.length) return _drawNode && _drawNode.id; // no assign
56457 return utilRebind(drawWay, dispatch$1, 'on');
56460 function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
56465 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawLine', function () {
56466 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.lines'))();
56468 mode.wayID = wayID;
56469 mode.isContinuing = continuing;
56471 mode.enter = function () {
56472 behavior.nodeIndex(affix === 'prefix' ? 0 : undefined);
56473 context.install(behavior);
56476 mode.exit = function () {
56477 context.uninstall(behavior);
56480 mode.selectedIDs = function () {
56484 mode.activeID = function () {
56485 return behavior && behavior.activeID() || [];
56491 function operationContinue(context, selectedIDs) {
56492 var _entities = selectedIDs.map(function (id) {
56493 return context.graph().entity(id);
56496 var _geometries = Object.assign({
56499 }, utilArrayGroupBy(_entities, function (entity) {
56500 return entity.geometry(context.graph());
56503 var _vertex = _geometries.vertex.length && _geometries.vertex[0];
56505 function candidateWays() {
56506 return _vertex ? context.graph().parentWays(_vertex).filter(function (parent) {
56507 return parent.geometry(context.graph()) === 'line' && !parent.isClosed() && parent.affix(_vertex.id) && (_geometries.line.length === 0 || _geometries.line[0] === parent);
56511 var _candidates = candidateWays();
56513 var operation = function operation() {
56514 var candidate = _candidates[0];
56515 context.enter(modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(_vertex.id), true));
56518 operation.relatedEntityIds = function () {
56519 return _candidates.length ? [_candidates[0].id] : [];
56522 operation.available = function () {
56523 return _geometries.vertex.length === 1 && _geometries.line.length <= 1 && !context.features().hasHiddenConnections(_vertex, context.graph());
56526 operation.disabled = function () {
56527 if (_candidates.length === 0) {
56528 return 'not_eligible';
56529 } else if (_candidates.length > 1) {
56536 operation.tooltip = function () {
56537 var disable = operation.disabled();
56538 return disable ? _t('operations.continue.' + disable) : _t('operations.continue.description');
56541 operation.annotation = function () {
56542 return _t('operations.continue.annotation.line');
56545 operation.id = 'continue';
56546 operation.keys = [_t('operations.continue.key')];
56547 operation.title = _t('operations.continue.title');
56548 operation.behavior = behaviorOperation(context).which(operation);
56552 function operationCopy(context, selectedIDs) {
56553 function getFilteredIdsToCopy() {
56554 return selectedIDs.filter(function (selectedID) {
56555 var entity = context.graph().hasEntity(selectedID); // don't copy untagged vertices separately from ways
56557 return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
56561 var operation = function operation() {
56562 var graph = context.graph();
56563 var selected = groupEntities(getFilteredIdsToCopy(), graph);
56569 for (i = 0; i < selected.relation.length; i++) {
56570 entity = selected.relation[i];
56572 if (!skip[entity.id] && entity.isComplete(graph)) {
56573 canCopy.push(entity.id);
56574 skip = getDescendants(entity.id, graph, skip);
56578 for (i = 0; i < selected.way.length; i++) {
56579 entity = selected.way[i];
56581 if (!skip[entity.id]) {
56582 canCopy.push(entity.id);
56583 skip = getDescendants(entity.id, graph, skip);
56587 for (i = 0; i < selected.node.length; i++) {
56588 entity = selected.node[i];
56590 if (!skip[entity.id]) {
56591 canCopy.push(entity.id);
56595 context.copyIDs(canCopy);
56597 if (_point && (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
56598 // store the anchor coordinates if copying more than a single node
56599 context.copyLonLat(context.projection.invert(_point));
56601 context.copyLonLat(null);
56605 function groupEntities(ids, graph) {
56606 var entities = ids.map(function (id) {
56607 return graph.entity(id);
56609 return Object.assign({
56613 }, utilArrayGroupBy(entities, 'type'));
56616 function getDescendants(id, graph, descendants) {
56617 var entity = graph.entity(id);
56619 descendants = descendants || {};
56621 if (entity.type === 'relation') {
56622 children = entity.members.map(function (m) {
56625 } else if (entity.type === 'way') {
56626 children = entity.nodes;
56631 for (var i = 0; i < children.length; i++) {
56632 if (!descendants[children[i]]) {
56633 descendants[children[i]] = true;
56634 descendants = getDescendants(children[i], graph, descendants);
56638 return descendants;
56641 operation.available = function () {
56642 return getFilteredIdsToCopy().length > 0;
56645 operation.disabled = function () {
56646 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
56648 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
56649 return 'too_large';
56655 operation.availableForKeypress = function () {
56656 var selection = window.getSelection && window.getSelection(); // if the user has text selected then let them copy that, not the selected feature
56658 return !selection || !selection.toString();
56661 operation.tooltip = function () {
56662 var disable = operation.disabled();
56663 return disable ? _t('operations.copy.' + disable, {
56664 n: selectedIDs.length
56665 }) : _t('operations.copy.description', {
56666 n: selectedIDs.length
56670 operation.annotation = function () {
56671 return _t('operations.copy.annotation', {
56672 n: selectedIDs.length
56678 operation.point = function (val) {
56683 operation.id = 'copy';
56684 operation.keys = [uiCmd('⌘C')];
56685 operation.title = _t('operations.copy.title');
56686 operation.behavior = behaviorOperation(context).which(operation);
56690 function operationDisconnect(context, selectedIDs) {
56691 var _vertexIDs = [];
56693 var _otherIDs = [];
56695 selectedIDs.forEach(function (id) {
56696 var entity = context.entity(id);
56698 if (entity.type === 'way') {
56700 } else if (entity.geometry(context.graph()) === 'vertex') {
56701 _vertexIDs.push(id);
56703 _otherIDs.push(id);
56708 _descriptionID = '',
56709 _annotationID = 'features';
56711 var _disconnectingVertexIds = [];
56712 var _disconnectingWayIds = [];
56714 if (_vertexIDs.length > 0) {
56715 // At the selected vertices, disconnect the selected ways, if any, else
56716 // disconnect all connected ways
56717 _disconnectingVertexIds = _vertexIDs;
56719 _vertexIDs.forEach(function (vertexID) {
56720 var action = actionDisconnect(vertexID);
56722 if (_wayIDs.length > 0) {
56723 var waysIDsForVertex = _wayIDs.filter(function (wayID) {
56724 var way = context.entity(wayID);
56725 return way.nodes.indexOf(vertexID) !== -1;
56728 action.limitWays(waysIDsForVertex);
56731 _actions.push(action);
56733 _disconnectingWayIds = _disconnectingWayIds.concat(context.graph().parentWays(context.graph().entity(vertexID)).map(function (d) {
56738 _disconnectingWayIds = utilArrayUniq(_disconnectingWayIds).filter(function (id) {
56739 return _wayIDs.indexOf(id) === -1;
56741 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
56743 if (_wayIDs.length === 1) {
56744 _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
56746 _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
56748 } else if (_wayIDs.length > 0) {
56749 // Disconnect the selected ways from each other, if they're connected,
56750 // else disconnect them from all connected ways
56751 var ways = _wayIDs.map(function (id) {
56752 return context.entity(id);
56755 var nodes = utilGetAllNodes(_wayIDs, context.graph());
56756 _coords = nodes.map(function (n) {
56758 }); // actions for connected nodes shared by at least two selected ways
56760 var sharedActions = [];
56761 var sharedNodes = []; // actions for connected nodes
56763 var unsharedActions = [];
56764 var unsharedNodes = [];
56765 nodes.forEach(function (node) {
56766 var action = actionDisconnect(node.id).limitWays(_wayIDs);
56768 if (action.disabled(context.graph()) !== 'not_connected') {
56771 for (var i in ways) {
56774 if (way.nodes.indexOf(node.id) !== -1) {
56778 if (count > 1) break;
56782 sharedActions.push(action);
56783 sharedNodes.push(node);
56785 unsharedActions.push(action);
56786 unsharedNodes.push(node);
56790 _descriptionID += 'no_points.';
56791 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
56793 if (sharedActions.length) {
56794 // if any nodes are shared, only disconnect the selected ways from each other
56795 _actions = sharedActions;
56796 _disconnectingVertexIds = sharedNodes.map(function (node) {
56799 _descriptionID += 'conjoined';
56800 _annotationID = 'from_each_other';
56802 // if no nodes are shared, disconnect the selected ways from all connected ways
56803 _actions = unsharedActions;
56804 _disconnectingVertexIds = unsharedNodes.map(function (node) {
56808 if (_wayIDs.length === 1) {
56809 _descriptionID += context.graph().geometry(_wayIDs[0]);
56811 _descriptionID += 'separate';
56816 var _extent = utilTotalExtent(_disconnectingVertexIds, context.graph());
56818 var operation = function operation() {
56819 context.perform(function (graph) {
56820 return _actions.reduce(function (graph, action) {
56821 return action(graph);
56823 }, operation.annotation());
56824 context.validator().validate();
56827 operation.relatedEntityIds = function () {
56828 if (_vertexIDs.length) {
56829 return _disconnectingWayIds;
56832 return _disconnectingVertexIds;
56835 operation.available = function () {
56836 if (_actions.length === 0) return false;
56837 if (_otherIDs.length !== 0) return false;
56838 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function (wayID) {
56839 return _vertexIDs.some(function (vertexID) {
56840 var way = context.entity(wayID);
56841 return way.nodes.indexOf(vertexID) !== -1;
56847 operation.disabled = function () {
56850 for (var actionIndex in _actions) {
56851 reason = _actions[actionIndex].disabled(context.graph());
56852 if (reason) return reason;
56855 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
56856 return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
56857 } else if (_coords && someMissing()) {
56858 return 'not_downloaded';
56859 } else if (selectedIDs.some(context.hasHiddenConnections)) {
56860 return 'connected_to_hidden';
56865 function someMissing() {
56866 if (context.inIntro()) return false;
56867 var osm = context.connection();
56870 var missing = _coords.filter(function (loc) {
56871 return !osm.isDataLoaded(loc);
56874 if (missing.length) {
56875 missing.forEach(function (loc) {
56876 context.loadTileAtLoc(loc);
56886 operation.tooltip = function () {
56887 var disable = operation.disabled();
56890 return _t('operations.disconnect.' + disable);
56893 return _t('operations.disconnect.description.' + _descriptionID);
56896 operation.annotation = function () {
56897 return _t('operations.disconnect.annotation.' + _annotationID);
56900 operation.id = 'disconnect';
56901 operation.keys = [_t('operations.disconnect.key')];
56902 operation.title = _t('operations.disconnect.title');
56903 operation.behavior = behaviorOperation(context).which(operation);
56907 function operationDowngrade(context, selectedIDs) {
56908 var _affectedFeatureCount = 0;
56910 var _downgradeType = downgradeTypeForEntityIDs(selectedIDs);
56912 var _multi = _affectedFeatureCount === 1 ? 'single' : 'multiple';
56914 function downgradeTypeForEntityIDs(entityIds) {
56916 _affectedFeatureCount = 0;
56918 for (var i in entityIds) {
56919 var entityID = entityIds[i];
56920 var type = downgradeTypeForEntityID(entityID);
56923 _affectedFeatureCount += 1;
56925 if (downgradeType && type !== downgradeType) {
56926 if (downgradeType !== 'generic' && type !== 'generic') {
56927 downgradeType = 'building_address';
56929 downgradeType = 'generic';
56932 downgradeType = type;
56937 return downgradeType;
56940 function downgradeTypeForEntityID(entityID) {
56941 var graph = context.graph();
56942 var entity = graph.entity(entityID);
56943 var preset = _mainPresetIndex.match(entity, graph);
56944 if (!preset || preset.isFallback()) return null;
56946 if (entity.type === 'node' && preset.id !== 'address' && Object.keys(entity.tags).some(function (key) {
56947 return key.match(/^addr:.{1,}/);
56952 var geometry = entity.geometry(graph);
56954 if (geometry === 'area' && entity.tags.building && !preset.tags.building) {
56958 if (geometry === 'vertex' && Object.keys(entity.tags).length) {
56965 var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
56966 var addressKeysToKeep = ['source'];
56968 var operation = function operation() {
56969 context.perform(function (graph) {
56970 for (var i in selectedIDs) {
56971 var entityID = selectedIDs[i];
56972 var type = downgradeTypeForEntityID(entityID);
56973 if (!type) continue;
56974 var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy
56976 for (var key in tags) {
56977 if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
56979 if (type === 'building') {
56980 if (buildingKeysToKeep.indexOf(key) !== -1 || key.match(/^building:.{1,}/) || key.match(/^roof:.{1,}/)) continue;
56983 if (type !== 'generic') {
56984 if (key.match(/^addr:.{1,}/) || key.match(/^source:.{1,}/)) continue;
56990 graph = actionChangeTags(entityID, tags)(graph);
56994 }, operation.annotation());
56995 context.validator().validate(); // refresh the select mode to enable the delete operation
56997 context.enter(modeSelect(context, selectedIDs));
57000 operation.available = function () {
57001 return _downgradeType;
57004 operation.disabled = function () {
57005 if (selectedIDs.some(hasWikidataTag)) {
57006 return 'has_wikidata_tag';
57011 function hasWikidataTag(id) {
57012 var entity = context.entity(id);
57013 return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
57017 operation.tooltip = function () {
57018 var disable = operation.disabled();
57019 return disable ? _t('operations.downgrade.' + disable + '.' + _multi) : _t('operations.downgrade.description.' + _downgradeType);
57022 operation.annotation = function () {
57025 if (_downgradeType === 'building_address') {
57026 suffix = 'generic';
57028 suffix = _downgradeType;
57031 return _t('operations.downgrade.annotation.' + suffix, {
57032 n: _affectedFeatureCount
57036 operation.id = 'downgrade';
57037 operation.keys = [uiCmd('⌫')];
57038 operation.title = _t('operations.downgrade.title');
57039 operation.behavior = behaviorOperation(context).which(operation);
57043 function operationExtract(context, selectedIDs) {
57044 var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
57046 var _geometries = utilArrayUniq(selectedIDs.map(function (entityID) {
57047 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
57048 }).filter(Boolean));
57050 var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
57054 var _actions = selectedIDs.map(function (entityID) {
57055 var graph = context.graph();
57056 var entity = graph.hasEntity(entityID);
57057 if (!entity || !entity.hasInterestingTags()) return null;
57058 if (entity.type === 'node' && graph.parentWays(entity).length === 0) return null;
57060 if (entity.type !== 'node') {
57061 var preset = _mainPresetIndex.match(entity, graph); // only allow extraction from ways/relations if the preset supports points
57063 if (preset.geometry.indexOf('point') === -1) return null;
57066 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
57067 return actionExtract(entityID);
57068 }).filter(Boolean);
57070 var operation = function operation() {
57071 var combinedAction = function combinedAction(graph) {
57072 _actions.forEach(function (action) {
57073 graph = action(graph);
57079 context.perform(combinedAction, operation.annotation()); // do the extract
57081 var extractedNodeIDs = _actions.map(function (action) {
57082 return action.getExtractedNodeID();
57085 context.enter(modeSelect(context, extractedNodeIDs));
57088 operation.available = function () {
57089 return _actions.length && selectedIDs.length === _actions.length;
57092 operation.disabled = function () {
57093 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
57094 return 'too_large';
57095 } else if (selectedIDs.some(function (entityID) {
57096 return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
57098 return 'connected_to_hidden';
57104 operation.tooltip = function () {
57105 var disableReason = operation.disabled();
57107 if (disableReason) {
57108 return _t('operations.extract.' + disableReason + '.' + _amount);
57110 return _t('operations.extract.description.' + _geometryID + '.' + _amount);
57114 operation.annotation = function () {
57115 return _t('operations.extract.annotation', {
57116 n: selectedIDs.length
57120 operation.id = 'extract';
57121 operation.keys = [_t('operations.extract.key')];
57122 operation.title = _t('operations.extract.title');
57123 operation.behavior = behaviorOperation(context).which(operation);
57127 function operationMerge(context, selectedIDs) {
57128 var _action = getAction();
57130 function getAction() {
57131 // prefer a non-disabled action first
57132 var join = actionJoin(selectedIDs);
57133 if (!join.disabled(context.graph())) return join;
57134 var merge = actionMerge(selectedIDs);
57135 if (!merge.disabled(context.graph())) return merge;
57136 var mergePolygon = actionMergePolygon(selectedIDs);
57137 if (!mergePolygon.disabled(context.graph())) return mergePolygon;
57138 var mergeNodes = actionMergeNodes(selectedIDs);
57139 if (!mergeNodes.disabled(context.graph())) return mergeNodes; // otherwise prefer an action with an interesting disabled reason
57141 if (join.disabled(context.graph()) !== 'not_eligible') return join;
57142 if (merge.disabled(context.graph()) !== 'not_eligible') return merge;
57143 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') return mergePolygon;
57147 var operation = function operation() {
57148 if (operation.disabled()) return;
57149 context.perform(_action, operation.annotation());
57150 context.validator().validate();
57151 var resultIDs = selectedIDs.filter(context.hasEntity);
57153 if (resultIDs.length > 1) {
57154 var interestingIDs = resultIDs.filter(function (id) {
57155 return context.entity(id).hasInterestingTags();
57157 if (interestingIDs.length) resultIDs = interestingIDs;
57160 context.enter(modeSelect(context, resultIDs));
57163 operation.available = function () {
57164 return selectedIDs.length >= 2;
57167 operation.disabled = function () {
57168 var actionDisabled = _action.disabled(context.graph());
57170 if (actionDisabled) return actionDisabled;
57171 var osm = context.connection();
57173 if (osm && _action.resultingWayNodesLength && _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
57174 return 'too_many_vertices';
57180 operation.tooltip = function () {
57181 var disabled = operation.disabled();
57184 if (disabled === 'restriction') {
57185 return _t('operations.merge.restriction', {
57186 relation: _mainPresetIndex.item('type/restriction').name()
57190 return _t('operations.merge.' + disabled);
57193 return _t('operations.merge.description');
57196 operation.annotation = function () {
57197 return _t('operations.merge.annotation', {
57198 n: selectedIDs.length
57202 operation.id = 'merge';
57203 operation.keys = [_t('operations.merge.key')];
57204 operation.title = _t('operations.merge.title');
57205 operation.behavior = behaviorOperation(context).which(operation);
57209 function operationPaste(context) {
57212 var operation = function operation() {
57213 if (!_pastePoint) return;
57214 var oldIDs = context.copyIDs();
57215 if (!oldIDs.length) return;
57216 var projection = context.projection;
57217 var extent = geoExtent();
57218 var oldGraph = context.copyGraph();
57220 var action = actionCopyEntities(oldIDs, oldGraph);
57221 context.perform(action);
57222 var copies = action.copies();
57223 var originals = new Set();
57224 Object.values(copies).forEach(function (entity) {
57225 originals.add(entity.id);
57228 for (var id in copies) {
57229 var oldEntity = oldGraph.entity(id);
57230 var newEntity = copies[id];
57232 extent._extend(oldEntity.extent(oldGraph)); // Exclude child nodes from newIDs if their parent way was also copied.
57235 var parents = context.graph().parentWays(newEntity);
57236 var parentCopied = parents.some(function (parent) {
57237 return originals.has(parent.id);
57240 if (!parentCopied) {
57241 newIDs.push(newEntity.id);
57243 } // Use the location of the copy operation to offset the paste location,
57244 // or else use the center of the pasted extent
57247 var copyPoint = context.copyLonLat() && projection(context.copyLonLat()) || projection(extent.center());
57248 var delta = geoVecSubtract(_pastePoint, copyPoint); // Move the pasted objects to be anchored at the paste location
57250 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
57251 context.enter(modeSelect(context, newIDs));
57254 operation.point = function (val) {
57259 operation.available = function () {
57260 return context.mode().id === 'browse';
57263 operation.disabled = function () {
57264 return !context.copyIDs().length;
57267 operation.tooltip = function () {
57268 var oldGraph = context.copyGraph();
57269 var ids = context.copyIDs();
57272 return _t('operations.paste.nothing_copied');
57275 return _t('operations.paste.description', {
57276 feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph),
57281 operation.annotation = function () {
57282 var ids = context.copyIDs();
57283 return _t('operations.paste.annotation', {
57288 operation.id = 'paste';
57289 operation.keys = [uiCmd('⌘V')];
57290 operation.title = _t('operations.paste.title');
57294 function operationReverse(context, selectedIDs) {
57295 var operation = function operation() {
57296 context.perform(function combinedReverseAction(graph) {
57297 actions().forEach(function (action) {
57298 graph = action(graph);
57301 }, operation.annotation());
57302 context.validator().validate();
57305 function actions(situation) {
57306 return selectedIDs.map(function (entityID) {
57307 var entity = context.hasEntity(entityID);
57308 if (!entity) return null;
57310 if (situation === 'toolbar') {
57311 if (entity.type === 'way' && !entity.isOneWay() && !entity.isSided()) return null;
57314 var geometry = entity.geometry(context.graph());
57315 if (entity.type !== 'node' && geometry !== 'line') return null;
57316 var action = actionReverse(entityID);
57317 if (action.disabled(context.graph())) return null;
57319 }).filter(Boolean);
57322 function reverseTypeID() {
57323 var acts = actions();
57324 var nodeActionCount = acts.filter(function (act) {
57325 var entity = context.hasEntity(act.entityID());
57326 return entity && entity.type === 'node';
57328 if (nodeActionCount === 0) return 'line';
57329 if (nodeActionCount === acts.length) return 'point';
57333 operation.available = function (situation) {
57334 return actions(situation).length > 0;
57337 operation.disabled = function () {
57341 operation.tooltip = function () {
57342 return _t('operations.reverse.description.' + reverseTypeID());
57345 operation.annotation = function () {
57346 var acts = actions();
57347 return _t('operations.reverse.annotation.' + reverseTypeID(), {
57352 operation.id = 'reverse';
57353 operation.keys = [_t('operations.reverse.key')];
57354 operation.title = _t('operations.reverse.title');
57355 operation.behavior = behaviorOperation(context).which(operation);
57359 function operationSplit(context, selectedIDs) {
57360 var _vertexIds = selectedIDs.filter(function (id) {
57361 return context.graph().geometry(id) === 'vertex';
57364 var _selectedWayIds = selectedIDs.filter(function (id) {
57365 var entity = context.graph().hasEntity(id);
57366 return entity && entity.type === 'way';
57369 var _isAvailable = _vertexIds.length > 0 && _vertexIds.length + _selectedWayIds.length === selectedIDs.length;
57371 var _action = actionSplit(_vertexIds);
57374 var _geometry = 'feature';
57375 var _waysAmount = 'single';
57377 var _nodesAmount = _vertexIds.length === 1 ? 'single' : 'multiple';
57379 if (_isAvailable) {
57380 if (_selectedWayIds.length) _action.limitWays(_selectedWayIds);
57381 _ways = _action.ways(context.graph());
57382 var geometries = {};
57384 _ways.forEach(function (way) {
57385 geometries[way.geometry(context.graph())] = true;
57388 if (Object.keys(geometries).length === 1) {
57389 _geometry = Object.keys(geometries)[0];
57392 _waysAmount = _ways.length === 1 ? 'single' : 'multiple';
57395 var operation = function operation() {
57396 var difference = context.perform(_action, operation.annotation()); // select both the nodes and the ways so the mapper can immediately disconnect them if desired
57398 var idsToSelect = _vertexIds.concat(difference.extantIDs().filter(function (id) {
57399 // filter out relations that may have had member additions
57400 return context.entity(id).type === 'way';
57403 context.enter(modeSelect(context, idsToSelect));
57406 operation.relatedEntityIds = function () {
57407 return _selectedWayIds.length ? [] : _ways.map(function (way) {
57412 operation.available = function () {
57413 return _isAvailable;
57416 operation.disabled = function () {
57417 var reason = _action.disabled(context.graph());
57421 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57422 return 'connected_to_hidden';
57428 operation.tooltip = function () {
57429 var disable = operation.disabled();
57430 if (disable) return _t('operations.split.' + disable);
57431 return _t('operations.split.description.' + _geometry + '.' + _waysAmount + '.' + _nodesAmount + '_node');
57434 operation.annotation = function () {
57435 return _t('operations.split.annotation.' + _geometry, {
57440 operation.id = 'split';
57441 operation.keys = [_t('operations.split.key')];
57442 operation.title = _t('operations.split.title');
57443 operation.behavior = behaviorOperation(context).which(operation);
57447 function operationStraighten(context, selectedIDs) {
57448 var _wayIDs = selectedIDs.filter(function (id) {
57449 return id.charAt(0) === 'w';
57452 var _nodeIDs = selectedIDs.filter(function (id) {
57453 return id.charAt(0) === 'n';
57456 var _amount = (_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple';
57458 var _nodes = utilGetAllNodes(selectedIDs, context.graph());
57460 var _coords = _nodes.map(function (n) {
57464 var _extent = utilTotalExtent(selectedIDs, context.graph());
57466 var _action = chooseAction();
57470 function chooseAction() {
57471 // straighten selected nodes
57472 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
57473 _geometry = 'point';
57474 return actionStraightenNodes(_nodeIDs, context.projection); // straighten selected ways (possibly between range of 2 selected nodes)
57475 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
57476 var startNodeIDs = [];
57477 var endNodeIDs = [];
57479 for (var i = 0; i < selectedIDs.length; i++) {
57480 var entity = context.entity(selectedIDs[i]);
57482 if (entity.type === 'node') {
57484 } else if (entity.type !== 'way' || entity.isClosed()) {
57485 return null; // exit early, can't straighten these
57488 startNodeIDs.push(entity.first());
57489 endNodeIDs.push(entity.last());
57490 } // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
57493 startNodeIDs = startNodeIDs.filter(function (n) {
57494 return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
57496 endNodeIDs = endNodeIDs.filter(function (n) {
57497 return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
57498 }); // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
57500 if (utilArrayDifference(startNodeIDs, endNodeIDs).length + utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return null; // Ensure path contains at least 3 unique nodes
57502 var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph()).map(function (node) {
57505 if (wayNodeIDs.length <= 2) return null; // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
57507 if (_nodeIDs.length === 2 && (wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1)) return null;
57509 if (_nodeIDs.length) {
57510 // If we're only straightenting between two points, we only need that extent visible
57511 _extent = utilTotalExtent(_nodeIDs, context.graph());
57514 _geometry = 'line';
57515 return actionStraightenWay(selectedIDs, context.projection);
57521 function operation() {
57522 if (!_action) return;
57523 context.perform(_action, operation.annotation());
57524 window.setTimeout(function () {
57525 context.validator().validate();
57526 }, 300); // after any transition
57529 operation.available = function () {
57530 return Boolean(_action);
57533 operation.disabled = function () {
57534 var reason = _action.disabled(context.graph());
57538 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
57539 return 'too_large';
57540 } else if (someMissing()) {
57541 return 'not_downloaded';
57542 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57543 return 'connected_to_hidden';
57548 function someMissing() {
57549 if (context.inIntro()) return false;
57550 var osm = context.connection();
57553 var missing = _coords.filter(function (loc) {
57554 return !osm.isDataLoaded(loc);
57557 if (missing.length) {
57558 missing.forEach(function (loc) {
57559 context.loadTileAtLoc(loc);
57569 operation.tooltip = function () {
57570 var disable = operation.disabled();
57571 return disable ? _t('operations.straighten.' + disable + '.' + _amount) : _t('operations.straighten.description.' + _geometry + (_wayIDs.length === 1 ? '' : 's'));
57574 operation.annotation = function () {
57575 return _t('operations.straighten.annotation.' + _geometry, {
57576 n: _wayIDs.length ? _wayIDs.length : _nodeIDs.length
57580 operation.id = 'straighten';
57581 operation.keys = [_t('operations.straighten.key')];
57582 operation.title = _t('operations.straighten.title');
57583 operation.behavior = behaviorOperation(context).which(operation);
57587 var Operations = /*#__PURE__*/Object.freeze({
57589 operationCircularize: operationCircularize,
57590 operationContinue: operationContinue,
57591 operationCopy: operationCopy,
57592 operationDelete: operationDelete,
57593 operationDisconnect: operationDisconnect,
57594 operationDowngrade: operationDowngrade,
57595 operationExtract: operationExtract,
57596 operationMerge: operationMerge,
57597 operationMove: operationMove,
57598 operationOrthogonalize: operationOrthogonalize,
57599 operationPaste: operationPaste,
57600 operationReflectShort: operationReflectShort,
57601 operationReflectLong: operationReflectLong,
57602 operationReverse: operationReverse,
57603 operationRotate: operationRotate,
57604 operationSplit: operationSplit,
57605 operationStraighten: operationStraighten
57608 var _relatedParent;
57610 function modeSelect(context, selectedIDs) {
57615 var keybinding = utilKeybinding('select');
57617 var _breatheBehavior = behaviorBreathe();
57619 var _modeDragNode = modeDragNode(context);
57621 var _selectBehavior;
57623 var _behaviors = [];
57624 var _operations = [];
57625 var _newFeature = false;
57626 var _follow = false;
57628 function singular() {
57629 if (selectedIDs && selectedIDs.length === 1) {
57630 return context.hasEntity(selectedIDs[0]);
57634 function selectedEntities() {
57635 return selectedIDs.map(function (id) {
57636 return context.hasEntity(id);
57637 }).filter(Boolean);
57640 function checkSelectedIDs() {
57643 if (Array.isArray(selectedIDs)) {
57644 ids = selectedIDs.filter(function (id) {
57645 return context.hasEntity(id);
57650 context.enter(modeBrowse(context));
57652 } else if (selectedIDs.length > 1 && ids.length === 1 || selectedIDs.length === 1 && ids.length > 1) {
57653 // switch between single- and multi-select UI
57654 context.enter(modeSelect(context, ids));
57660 } // find the common parent ways for nextVertex, previousVertex
57663 function commonParents() {
57664 var graph = context.graph();
57665 var commonParents = [];
57667 for (var i = 0; i < selectedIDs.length; i++) {
57668 var entity = context.hasEntity(selectedIDs[i]);
57670 if (!entity || entity.geometry(graph) !== 'vertex') {
57671 return []; // selection includes some not vertices
57674 var currParents = graph.parentWays(entity).map(function (w) {
57678 if (!commonParents.length) {
57679 commonParents = currParents;
57683 commonParents = utilArrayIntersection(commonParents, currParents);
57685 if (!commonParents.length) {
57690 return commonParents;
57693 function singularParent() {
57694 var parents = commonParents();
57696 if (!parents || parents.length === 0) {
57697 _relatedParent = null;
57699 } // relatedParent is used when we visit a vertex with multiple
57700 // parents, and we want to remember which parent line we started on.
57703 if (parents.length === 1) {
57704 _relatedParent = parents[0]; // remember this parent for later
57706 return _relatedParent;
57709 if (parents.indexOf(_relatedParent) !== -1) {
57710 return _relatedParent; // prefer the previously seen parent
57716 mode.selectedIDs = function (val) {
57717 if (!arguments.length) return selectedIDs;
57722 mode.zoomToSelected = function () {
57723 context.map().zoomToEase(selectedEntities());
57726 mode.newFeature = function (val) {
57727 if (!arguments.length) return _newFeature;
57732 mode.selectBehavior = function (val) {
57733 if (!arguments.length) return _selectBehavior;
57734 _selectBehavior = val;
57738 mode.follow = function (val) {
57739 if (!arguments.length) return _follow;
57744 function loadOperations() {
57745 _operations.forEach(function (operation) {
57746 if (operation.behavior) {
57747 context.uninstall(operation.behavior);
57751 _operations = Object.values(Operations).map(function (o) {
57752 return o(context, selectedIDs);
57753 }).filter(function (o) {
57754 return o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy';
57755 }).concat([// group copy/downgrade/delete operation together at the end of the list
57756 operationCopy(context, selectedIDs), operationDowngrade(context, selectedIDs), operationDelete(context, selectedIDs)]).filter(function (operation) {
57757 return operation.available();
57760 _operations.forEach(function (operation) {
57761 if (operation.behavior) {
57762 context.install(operation.behavior);
57764 }); // remove any displayed menu
57767 context.ui().closeEditMenu();
57770 mode.operations = function () {
57771 return _operations;
57774 mode.enter = function () {
57775 if (!checkSelectedIDs()) return;
57776 context.features().forceVisible(selectedIDs);
57778 _modeDragNode.restoreSelectedIDs(selectedIDs);
57782 if (!_behaviors.length) {
57783 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
57784 _behaviors = [behaviorPaste(context), _breatheBehavior, behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect), _selectBehavior, behaviorLasso(context), _modeDragNode.behavior, modeDragNote(context).behavior];
57787 _behaviors.forEach(context.install);
57789 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) {
57790 return uiCmd('⇧' + key);
57791 }), scaleSelection(1.05)).on(utilKeybinding.plusKeys.map(function (key) {
57792 return uiCmd('⇧⌥' + key);
57793 }), scaleSelection(Math.pow(1.05, 5))).on(utilKeybinding.minusKeys.map(function (key) {
57794 return uiCmd('⇧' + key);
57795 }), scaleSelection(1 / 1.05)).on(utilKeybinding.minusKeys.map(function (key) {
57796 return uiCmd('⇧⌥' + key);
57797 }), scaleSelection(1 / Math.pow(1.05, 5))).on(['\\', 'pause'], nextParent).on('⎋', esc, true);
57798 select(document).call(keybinding);
57799 context.ui().sidebar.select(selectedIDs, _newFeature);
57800 context.history().on('change.select', function () {
57801 loadOperations(); // reselect after change in case relation members were removed or added
57804 }).on('undone.select', checkSelectedIDs).on('redone.select', checkSelectedIDs);
57805 context.map().on('drawn.select', selectElements).on('crossEditableZoom.select', function () {
57808 _breatheBehavior.restartIfNeeded(context.surface());
57810 context.map().doubleUpHandler().on('doubleUp.modeSelect', didDoubleUp);
57814 var extent = geoExtent();
57815 var graph = context.graph();
57816 selectedIDs.forEach(function (id) {
57817 var entity = context.entity(id);
57819 extent._extend(entity.extent(graph));
57821 var loc = extent.center();
57822 context.map().centerEase(loc); // we could enter the mode multiple times, so reset follow for next time
57827 function nudgeSelection(delta) {
57828 return function () {
57829 // prevent nudging during low zoom selection
57830 if (!context.map().withinEditableZoom()) return;
57831 var moveOp = operationMove(context, selectedIDs);
57833 if (moveOp.disabled()) {
57834 context.ui().flash.duration(4000).iconName('#iD-operation-' + moveOp.id).iconClass('operation disabled').label(moveOp.tooltip)();
57836 context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
57837 context.validator().validate();
57842 function scaleSelection(factor) {
57843 return function () {
57844 // prevent scaling during low zoom selection
57845 if (!context.map().withinEditableZoom()) return;
57846 var nodes = utilGetAllNodes(selectedIDs, context.graph());
57847 var isUp = factor > 1; // can only scale if multiple nodes are selected
57849 if (nodes.length <= 1) return;
57850 var extent = utilTotalExtent(selectedIDs, context.graph()); // These disabled checks would normally be handled by an operation
57851 // object, but we don't want an actual scale operation at this point.
57853 function scalingDisabled() {
57855 return 'too_small';
57856 } else if (extent.percentContainedIn(context.map().extent()) < 0.8) {
57857 return 'too_large';
57858 } else if (someMissing() || selectedIDs.some(incompleteRelation)) {
57859 return 'not_downloaded';
57860 } else if (selectedIDs.some(context.hasHiddenConnections)) {
57861 return 'connected_to_hidden';
57866 function tooSmall() {
57867 if (isUp) return false;
57868 var dLon = Math.abs(extent[1][0] - extent[0][0]);
57869 var dLat = Math.abs(extent[1][1] - extent[0][1]);
57870 return dLon < geoMetersToLon(1, extent[1][1]) && dLat < geoMetersToLat(1);
57873 function someMissing() {
57874 if (context.inIntro()) return false;
57875 var osm = context.connection();
57878 var missing = nodes.filter(function (n) {
57879 return !osm.isDataLoaded(n.loc);
57882 if (missing.length) {
57883 missing.forEach(function (loc) {
57884 context.loadTileAtLoc(loc);
57893 function incompleteRelation(id) {
57894 var entity = context.entity(id);
57895 return entity.type === 'relation' && !entity.isComplete(context.graph());
57899 var disabled = scalingDisabled();
57902 var multi = selectedIDs.length === 1 ? 'single' : 'multiple';
57903 context.ui().flash.duration(4000).iconName('#iD-icon-no').iconClass('operation disabled').label(_t('operations.scale.' + disabled + '.' + multi))();
57905 var pivot = context.projection(extent.center());
57906 var annotation = _t('operations.scale.annotation.' + (isUp ? 'up' : 'down') + '.feature', {
57907 n: selectedIDs.length
57909 context.perform(actionScale(selectedIDs, pivot, factor, context.projection), annotation);
57910 context.validator().validate();
57915 function didDoubleUp(d3_event, loc) {
57916 if (!context.map().withinEditableZoom()) return;
57917 var target = select(d3_event.target);
57918 var datum = target.datum();
57919 var entity = datum && datum.properties && datum.properties.entity;
57920 if (!entity) return;
57922 if (entity instanceof osmWay && target.classed('target')) {
57923 var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
57924 var prev = entity.nodes[choice.index - 1];
57925 var next = entity.nodes[choice.index];
57926 context.perform(actionAddMidpoint({
57929 }, osmNode()), _t('operations.add.annotation.vertex'));
57930 } else if (entity.type === 'midpoint') {
57931 context.perform(actionAddMidpoint({
57934 }, osmNode()), _t('operations.add.annotation.vertex'));
57938 function selectElements() {
57939 if (!checkSelectedIDs()) return;
57940 var surface = context.surface();
57941 surface.selectAll('.selected-member').classed('selected-member', false);
57942 surface.selectAll('.selected').classed('selected', false);
57943 surface.selectAll('.related').classed('related', false);
57946 if (_relatedParent) {
57947 surface.selectAll(utilEntitySelector([_relatedParent])).classed('related', true);
57950 if (context.map().withinEditableZoom()) {
57951 // Apply selection styling if not in wide selection
57952 surface.selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true
57953 /* skipMultipolgonMembers */
57954 )).classed('selected-member', true);
57955 surface.selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph())).classed('selected', true);
57960 if (context.container().select('.combobox').size()) return;
57961 context.enter(modeBrowse(context));
57964 function firstVertex(d3_event) {
57965 d3_event.preventDefault();
57966 var entity = singular();
57967 var parent = singularParent();
57970 if (entity && entity.type === 'way') {
57972 } else if (parent) {
57973 way = context.entity(parent);
57977 context.enter(modeSelect(context, [way.first()]).follow(true));
57981 function lastVertex(d3_event) {
57982 d3_event.preventDefault();
57983 var entity = singular();
57984 var parent = singularParent();
57987 if (entity && entity.type === 'way') {
57989 } else if (parent) {
57990 way = context.entity(parent);
57994 context.enter(modeSelect(context, [way.last()]).follow(true));
57998 function previousVertex(d3_event) {
57999 d3_event.preventDefault();
58000 var parent = singularParent();
58001 if (!parent) return;
58002 var way = context.entity(parent);
58003 var length = way.nodes.length;
58004 var curr = way.nodes.indexOf(selectedIDs[0]);
58009 } else if (way.isClosed()) {
58010 index = length - 2;
58013 if (index !== -1) {
58014 context.enter(modeSelect(context, [way.nodes[index]]).follow(true));
58018 function nextVertex(d3_event) {
58019 d3_event.preventDefault();
58020 var parent = singularParent();
58021 if (!parent) return;
58022 var way = context.entity(parent);
58023 var length = way.nodes.length;
58024 var curr = way.nodes.indexOf(selectedIDs[0]);
58027 if (curr < length - 1) {
58029 } else if (way.isClosed()) {
58033 if (index !== -1) {
58034 context.enter(modeSelect(context, [way.nodes[index]]).follow(true));
58038 function nextParent(d3_event) {
58039 d3_event.preventDefault();
58040 var parents = commonParents();
58041 if (!parents || parents.length < 2) return;
58042 var index = parents.indexOf(_relatedParent);
58044 if (index < 0 || index > parents.length - 2) {
58045 _relatedParent = parents[0];
58047 _relatedParent = parents[index + 1];
58050 var surface = context.surface();
58051 surface.selectAll('.related').classed('related', false);
58053 if (_relatedParent) {
58054 surface.selectAll(utilEntitySelector([_relatedParent])).classed('related', true);
58059 mode.exit = function () {
58060 _newFeature = false;
58062 _operations.forEach(function (operation) {
58063 if (operation.behavior) {
58064 context.uninstall(operation.behavior);
58070 _behaviors.forEach(context.uninstall);
58072 select(document).call(keybinding.unbind);
58073 context.ui().closeEditMenu();
58074 context.history().on('change.select', null).on('undone.select', null).on('redone.select', null);
58075 var surface = context.surface();
58076 surface.selectAll('.selected-member').classed('selected-member', false);
58077 surface.selectAll('.selected').classed('selected', false);
58078 surface.selectAll('.highlighted').classed('highlighted', false);
58079 surface.selectAll('.related').classed('related', false);
58080 context.map().on('drawn.select', null);
58081 context.ui().sidebar.hide();
58082 context.features().forceVisible([]);
58083 var entity = singular();
58085 if (_newFeature && entity && entity.type === 'relation' && // no tags
58086 Object.keys(entity.tags).length === 0 && // no parent relations
58087 context.graph().parentRelations(entity).length === 0 && ( // no members or one member with no role
58088 entity.members.length === 0 || entity.members.length === 1 && !entity.members[0].role)) {
58089 // the user added this relation but didn't edit it at all, so just delete it
58090 var deleteAction = actionDeleteRelation(entity.id, true
58091 /* don't delete untagged members */
58093 context.perform(deleteAction, _t('operations.delete.annotation.relation'));
58100 function uiLasso(context) {
58101 var group, polygon;
58102 lasso.coordinates = [];
58104 function lasso(selection) {
58105 context.container().classed('lasso', true);
58106 group = selection.append('g').attr('class', 'lasso hide');
58107 polygon = group.append('path').attr('class', 'lasso-path');
58108 group.call(uiToggle(true));
58113 polygon.data([lasso.coordinates]).attr('d', function (d) {
58114 return 'M' + d.join(' L') + ' Z';
58119 lasso.extent = function () {
58120 return lasso.coordinates.reduce(function (extent, point) {
58121 return extent.extend(geoExtent(point));
58125 lasso.p = function (_) {
58126 if (!arguments.length) return lasso;
58127 lasso.coordinates.push(_);
58132 lasso.close = function () {
58134 group.call(uiToggle(false, function () {
58135 select(this).remove();
58139 context.container().classed('lasso', false);
58145 function behaviorLasso(context) {
58146 // use pointer events on supported platforms; fallback to mouse events
58147 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
58149 var behavior = function behavior(selection) {
58152 function pointerdown(d3_event) {
58153 var button = 0; // left
58155 if (d3_event.button === button && d3_event.shiftKey === true) {
58157 select(window).on(_pointerPrefix + 'move.lasso', pointermove).on(_pointerPrefix + 'up.lasso', pointerup);
58158 d3_event.stopPropagation();
58162 function pointermove() {
58164 lasso = uiLasso(context);
58165 context.surface().call(lasso);
58168 lasso.p(context.map().mouse());
58171 function normalize(a, b) {
58172 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])]];
58175 function lassoed() {
58176 if (!lasso) return [];
58177 var graph = context.graph();
58180 if (context.map().editableDataEnabled(true
58181 /* skipZoomCheck */
58182 ) && context.map().isInWideSelection()) {
58183 // only select from the visible nodes
58184 limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
58185 } else if (!context.map().editableDataEnabled()) {
58189 var bounds = lasso.extent().map(context.projection.invert);
58190 var extent = geoExtent(normalize(bounds[0], bounds[1]));
58191 var intersects = context.history().intersects(extent).filter(function (entity) {
58192 return entity.type === 'node' && (!limitToNodes || limitToNodes.has(entity)) && geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) && !context.features().isHidden(entity, graph, entity.geometry(graph));
58193 }); // sort the lassoed nodes as best we can
58195 intersects.sort(function (node1, node2) {
58196 var parents1 = graph.parentWays(node1);
58197 var parents2 = graph.parentWays(node2);
58199 if (parents1.length && parents2.length) {
58200 // both nodes are vertices
58201 var sharedParents = utilArrayIntersection(parents1, parents2);
58203 if (sharedParents.length) {
58204 var sharedParentNodes = sharedParents[0].nodes; // vertices are members of the same way; sort them in their listed order
58206 return sharedParentNodes.indexOf(node1.id) - sharedParentNodes.indexOf(node2.id);
58208 // vertices do not share a way; group them by their respective parent ways
58209 return parseFloat(parents1[0].id.slice(1)) - parseFloat(parents2[0].id.slice(1));
58211 } else if (parents1.length || parents2.length) {
58212 // only one node is a vertex; sort standalone points before vertices
58213 return parents1.length - parents2.length;
58214 } // both nodes are standalone points; sort left to right
58217 return node1.loc[0] - node2.loc[0];
58219 return intersects.map(function (entity) {
58224 function pointerup() {
58225 select(window).on(_pointerPrefix + 'move.lasso', null).on(_pointerPrefix + 'up.lasso', null);
58226 if (!lasso) return;
58227 var ids = lassoed();
58231 context.enter(modeSelect(context, ids));
58235 selection.on(_pointerPrefix + 'down.lasso', pointerdown);
58238 behavior.off = function (selection) {
58239 selection.on(_pointerPrefix + 'down.lasso', null);
58245 function modeBrowse(context) {
58249 title: _t('modes.browse.title'),
58250 description: _t('modes.browse.description')
58254 var _selectBehavior;
58256 var _behaviors = [];
58258 mode.selectBehavior = function (val) {
58259 if (!arguments.length) return _selectBehavior;
58260 _selectBehavior = val;
58264 mode.enter = function () {
58265 if (!_behaviors.length) {
58266 if (!_selectBehavior) _selectBehavior = behaviorSelect(context);
58267 _behaviors = [behaviorPaste(context), behaviorHover(context).on('hover', context.ui().sidebar.hover), _selectBehavior, behaviorLasso(context), modeDragNode(context).behavior, modeDragNote(context).behavior];
58270 _behaviors.forEach(context.install); // Get focus on the body.
58273 if (document.activeElement && document.activeElement.blur) {
58274 document.activeElement.blur();
58278 context.ui().sidebar.show(sidebar);
58280 context.ui().sidebar.select(null);
58284 mode.exit = function () {
58285 context.ui().sidebar.hover.cancel();
58287 _behaviors.forEach(context.uninstall);
58290 context.ui().sidebar.hide();
58294 mode.sidebar = function (_) {
58295 if (!arguments.length) return sidebar;
58300 mode.operations = function () {
58301 return [operationPaste(context)];
58307 function behaviorAddWay(context) {
58308 var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
58309 var draw = behaviorDraw(context);
58311 function behavior(surface) {
58312 draw.on('click', function () {
58313 dispatch$1.apply('start', this, arguments);
58314 }).on('clickWay', function () {
58315 dispatch$1.apply('startFromWay', this, arguments);
58316 }).on('clickNode', function () {
58317 dispatch$1.apply('startFromNode', this, arguments);
58318 }).on('cancel', behavior.cancel).on('finish', behavior.cancel);
58319 context.map().dblclickZoomEnable(false);
58320 surface.call(draw);
58323 behavior.off = function (surface) {
58324 surface.call(draw.off);
58327 behavior.cancel = function () {
58328 window.setTimeout(function () {
58329 context.map().dblclickZoomEnable(true);
58331 context.enter(modeBrowse(context));
58334 return utilRebind(behavior, dispatch$1, 'on');
58337 function behaviorHash(context) {
58338 // cached window.location.hash
58339 var _cachedHash = null; // allowable latitude range
58341 var _latitudeLimit = 90 - 1e-8;
58343 function computedHashParameters() {
58344 var map = context.map();
58345 var center = map.center();
58346 var zoom = map.zoom();
58347 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
58348 var oldParams = utilObjectOmit(utilStringQs(window.location.hash), ['comment', 'source', 'hashtags', 'walkthrough']);
58349 var newParams = {};
58350 delete oldParams.id;
58351 var selected = context.selectedIDs().filter(function (id) {
58352 return context.hasEntity(id);
58355 if (selected.length) {
58356 newParams.id = selected.join(',');
58359 newParams.map = zoom.toFixed(2) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision);
58360 return Object.assign(oldParams, newParams);
58363 function computedHash() {
58364 return '#' + utilQsString(computedHashParameters(), true);
58367 function computedTitle(includeChangeCount) {
58368 var baseTitle = context.documentTitleBase() || 'iD';
58372 var selected = context.selectedIDs().filter(function (id) {
58373 return context.hasEntity(id);
58376 if (selected.length) {
58377 var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
58379 if (selected.length > 1) {
58380 contextual = _t('title.labeled_and_more', {
58381 labeled: firstLabel,
58382 count: selected.length - 1
58385 contextual = firstLabel;
58388 titleID = 'context';
58391 if (includeChangeCount) {
58392 changeCount = context.history().difference().summary().length;
58394 if (changeCount > 0) {
58395 titleID = contextual ? 'changes_context' : 'changes';
58400 return _t('title.format.' + titleID, {
58401 changes: changeCount,
58403 context: contextual
58410 function updateTitle(includeChangeCount) {
58411 if (!context.setsDocumentTitle()) return;
58412 var newTitle = computedTitle(includeChangeCount);
58414 if (document.title !== newTitle) {
58415 document.title = newTitle;
58419 function updateHashIfNeeded() {
58420 if (context.inIntro()) return;
58421 var latestHash = computedHash();
58423 if (_cachedHash !== latestHash) {
58424 _cachedHash = latestHash; // Update the URL hash without affecting the browser navigation stack,
58425 // though unavoidably creating a browser history entry
58427 window.history.replaceState(null, computedTitle(false
58428 /* includeChangeCount */
58429 ), latestHash); // set the title we want displayed for the browser tab/window
58432 /* includeChangeCount */
58437 var _throttledUpdate = throttle(updateHashIfNeeded, 500);
58439 var _throttledUpdateTitle = throttle(function () {
58441 /* includeChangeCount */
58445 function hashchange() {
58446 // ignore spurious hashchange events
58447 if (window.location.hash === _cachedHash) return;
58448 _cachedHash = window.location.hash;
58449 var q = utilStringQs(_cachedHash);
58450 var mapArgs = (q.map || '').split('/').map(Number);
58452 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
58453 // replace bogus hash
58454 updateHashIfNeeded();
58456 // don't update if the new hash already reflects the state of iD
58457 if (_cachedHash === computedHash()) return;
58458 var mode = context.mode();
58459 context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
58461 if (q.id && mode) {
58462 var ids = q.id.split(',').filter(function (id) {
58463 return context.hasEntity(id);
58466 if (ids.length && (mode.id === 'browse' || mode.id === 'select' && !utilArrayIdentical(mode.selectedIDs(), ids))) {
58467 context.enter(modeSelect(context, ids));
58472 var center = context.map().center();
58473 var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
58474 var maxdist = 500; // Don't allow the hash location to change too much while drawing
58475 // This can happen if the user accidentally hit the back button. #3996
58477 if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
58478 context.enter(modeBrowse(context));
58484 function behavior() {
58485 context.map().on('move.behaviorHash', _throttledUpdate);
58486 context.history().on('change.behaviorHash', _throttledUpdateTitle);
58487 context.on('enter.behaviorHash', _throttledUpdate);
58488 select(window).on('hashchange.behaviorHash', hashchange);
58490 if (window.location.hash) {
58491 var q = utilStringQs(window.location.hash);
58494 //if (!context.history().hasRestorableChanges()) {
58495 // targeting specific features: download, select, and zoom to them
58496 context.zoomToEntity(q.id.split(',')[0], !q.map); //}
58499 if (q.walkthrough === 'true') {
58500 behavior.startWalkthrough = true;
58504 behavior.hadHash = true;
58508 updateTitle(false);
58512 behavior.off = function () {
58513 _throttledUpdate.cancel();
58515 _throttledUpdateTitle.cancel();
58517 context.map().on('move.behaviorHash', null);
58518 context.on('enter.behaviorHash', null);
58519 select(window).on('hashchange.behaviorHash', null);
58520 window.location.hash = '';
58527 iD.coreDifference represents the difference between two graphs.
58528 It knows how to calculate the set of entities that were
58529 created, modified, or deleted, and also contains the logic
58530 for recursively extending a difference to the complete set
58531 of entities that will require a redraw, taking into account
58532 child and parent relationships.
58535 function coreDifference(base, head) {
58537 var _didChange = {}; // 'addition', 'deletion', 'geometry', 'properties'
58541 function checkEntityID(id) {
58542 var h = head.entities[id];
58543 var b = base.entities[id];
58544 if (h === b) return;
58545 if (_changes[id]) return;
58552 _didChange.deletion = true;
58561 _didChange.addition = true;
58566 if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
58571 _didChange.geometry = true;
58572 _didChange.properties = true;
58576 if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
58581 _didChange.geometry = true;
58584 if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
58589 _didChange.geometry = true;
58592 if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
58597 _didChange.properties = true;
58603 // HOT CODE: there can be many thousands of downloaded entities, so looping
58604 // through them all can become a performance bottleneck. Optimize by
58605 // resolving duplicates and using a basic `for` loop
58606 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
58608 for (var i = 0; i < ids.length; i++) {
58609 checkEntityID(ids[i]);
58615 _diff.length = function length() {
58616 return Object.keys(_changes).length;
58619 _diff.changes = function changes() {
58623 _diff.didChange = _didChange; // pass true to include affected relation members
58625 _diff.extantIDs = function extantIDs(includeRelMembers) {
58626 var result = new Set();
58627 Object.keys(_changes).forEach(function (id) {
58628 if (_changes[id].head) {
58632 var h = _changes[id].head;
58633 var b = _changes[id].base;
58634 var entity = h || b;
58636 if (includeRelMembers && entity.type === 'relation') {
58637 var mh = h ? h.members.map(function (m) {
58640 var mb = b ? b.members.map(function (m) {
58643 utilArrayUnion(mh, mb).forEach(function (memberID) {
58644 if (head.hasEntity(memberID)) {
58645 result.add(memberID);
58650 return Array.from(result);
58653 _diff.modified = function modified() {
58655 Object.values(_changes).forEach(function (change) {
58656 if (change.base && change.head) {
58657 result.push(change.head);
58663 _diff.created = function created() {
58665 Object.values(_changes).forEach(function (change) {
58666 if (!change.base && change.head) {
58667 result.push(change.head);
58673 _diff.deleted = function deleted() {
58675 Object.values(_changes).forEach(function (change) {
58676 if (change.base && !change.head) {
58677 result.push(change.base);
58683 _diff.summary = function summary() {
58685 var keys = Object.keys(_changes);
58687 for (var i = 0; i < keys.length; i++) {
58688 var change = _changes[keys[i]];
58690 if (change.head && change.head.geometry(head) !== 'vertex') {
58691 addEntity(change.head, head, change.base ? 'modified' : 'created');
58692 } else if (change.base && change.base.geometry(base) !== 'vertex') {
58693 addEntity(change.base, base, 'deleted');
58694 } else if (change.base && change.head) {
58696 var moved = !fastDeepEqual(change.base.loc, change.head.loc);
58697 var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
58700 addParents(change.head);
58703 if (retagged || moved && change.head.hasInterestingTags()) {
58704 addEntity(change.head, head, 'modified');
58706 } else if (change.head && change.head.hasInterestingTags()) {
58708 addEntity(change.head, head, 'created');
58709 } else if (change.base && change.base.hasInterestingTags()) {
58711 addEntity(change.base, base, 'deleted');
58715 return Object.values(relevant);
58717 function addEntity(entity, graph, changeType) {
58718 relevant[entity.id] = {
58721 changeType: changeType
58725 function addParents(entity) {
58726 var parents = head.parentWays(entity);
58728 for (var j = parents.length - 1; j >= 0; j--) {
58729 var parent = parents[j];
58731 if (!(parent.id in relevant)) {
58732 addEntity(parent, head, 'modified');
58736 }; // returns complete set of entities that require a redraw
58737 // (optionally within given `extent`)
58740 _diff.complete = function complete(extent) {
58744 for (id in _changes) {
58745 change = _changes[id];
58746 var h = change.head;
58747 var b = change.base;
58748 var entity = h || b;
58750 if (extent && (!h || !h.intersects(extent, head)) && (!b || !b.intersects(extent, base))) continue;
58753 if (entity.type === 'way') {
58754 var nh = h ? h.nodes : [];
58755 var nb = b ? b.nodes : [];
58757 diff = utilArrayDifference(nh, nb);
58759 for (i = 0; i < diff.length; i++) {
58760 result[diff[i]] = head.hasEntity(diff[i]);
58763 diff = utilArrayDifference(nb, nh);
58765 for (i = 0; i < diff.length; i++) {
58766 result[diff[i]] = head.hasEntity(diff[i]);
58770 if (entity.type === 'relation' && entity.isMultipolygon()) {
58771 var mh = h ? h.members.map(function (m) {
58774 var mb = b ? b.members.map(function (m) {
58777 var ids = utilArrayUnion(mh, mb);
58779 for (i = 0; i < ids.length; i++) {
58780 var member = head.hasEntity(ids[i]);
58781 if (!member) continue; // not downloaded
58783 if (extent && !member.intersects(extent, head)) continue; // not visible
58785 result[ids[i]] = member;
58789 addParents(head.parentWays(entity), result);
58790 addParents(head.parentRelations(entity), result);
58795 function addParents(parents, result) {
58796 for (var i = 0; i < parents.length; i++) {
58797 var parent = parents[i];
58798 if (parent.id in result) continue;
58799 result[parent.id] = parent;
58800 addParents(head.parentRelations(parent), result);
58808 function coreTree(head) {
58809 // tree for entities
58810 var _rtree = new RBush();
58812 var _bboxes = {}; // maintain a separate tree for granular way segments
58814 var _segmentsRTree = new RBush();
58816 var _segmentsBBoxes = {};
58817 var _segmentsByWayId = {};
58820 function entityBBox(entity) {
58821 var bbox = entity.extent(head).bbox();
58822 bbox.id = entity.id;
58823 _bboxes[entity.id] = bbox;
58827 function segmentBBox(segment) {
58828 var extent = segment.extent(head); // extent can be null if the node entities aren't in the graph for some reason
58830 if (!extent) return null;
58831 var bbox = extent.bbox();
58832 bbox.segment = segment;
58833 _segmentsBBoxes[segment.id] = bbox;
58837 function removeEntity(entity) {
58838 _rtree.remove(_bboxes[entity.id]);
58840 delete _bboxes[entity.id];
58842 if (_segmentsByWayId[entity.id]) {
58843 _segmentsByWayId[entity.id].forEach(function (segment) {
58844 _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
58846 delete _segmentsBBoxes[segment.id];
58849 delete _segmentsByWayId[entity.id];
58853 function loadEntities(entities) {
58854 _rtree.load(entities.map(entityBBox));
58857 entities.forEach(function (entity) {
58858 if (entity.segments) {
58859 var entitySegments = entity.segments(head); // cache these to make them easy to remove later
58861 _segmentsByWayId[entity.id] = entitySegments;
58862 segments = segments.concat(entitySegments);
58865 if (segments.length) _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean));
58868 function updateParents(entity, insertions, memo) {
58869 head.parentWays(entity).forEach(function (way) {
58870 if (_bboxes[way.id]) {
58872 insertions[way.id] = way;
58875 updateParents(way, insertions, memo);
58877 head.parentRelations(entity).forEach(function (relation) {
58878 if (memo[entity.id]) return;
58879 memo[entity.id] = true;
58881 if (_bboxes[relation.id]) {
58882 removeEntity(relation);
58883 insertions[relation.id] = relation;
58886 updateParents(relation, insertions, memo);
58890 tree.rebase = function (entities, force) {
58891 var insertions = {};
58893 for (var i = 0; i < entities.length; i++) {
58894 var entity = entities[i];
58895 if (!entity.visible) continue;
58897 if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
58900 } else if (_bboxes[entity.id]) {
58901 removeEntity(entity);
58905 insertions[entity.id] = entity;
58906 updateParents(entity, insertions, {});
58909 loadEntities(Object.values(insertions));
58913 function updateToGraph(graph) {
58914 if (graph === head) return;
58915 var diff = coreDifference(head, graph);
58917 var changed = diff.didChange;
58918 if (!changed.addition && !changed.deletion && !changed.geometry) return;
58919 var insertions = {};
58921 if (changed.deletion) {
58922 diff.deleted().forEach(function (entity) {
58923 removeEntity(entity);
58927 if (changed.geometry) {
58928 diff.modified().forEach(function (entity) {
58929 removeEntity(entity);
58930 insertions[entity.id] = entity;
58931 updateParents(entity, insertions, {});
58935 if (changed.addition) {
58936 diff.created().forEach(function (entity) {
58937 insertions[entity.id] = entity;
58941 loadEntities(Object.values(insertions));
58942 } // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
58945 tree.intersects = function (extent, graph) {
58946 updateToGraph(graph);
58947 return _rtree.search(extent.bbox()).map(function (bbox) {
58948 return graph.entity(bbox.id);
58950 }; // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
58953 tree.waySegments = function (extent, graph) {
58954 updateToGraph(graph);
58955 return _segmentsRTree.search(extent.bbox()).map(function (bbox) {
58956 return bbox.segment;
58963 function uiModal(selection, blocking) {
58966 var keybinding = utilKeybinding('modal');
58967 var previous = selection.select('div.modal');
58968 var animate = previous.empty();
58969 previous.transition().duration(200).style('opacity', 0).remove();
58970 var shaded = selection.append('div').attr('class', 'shaded').style('opacity', 0);
58972 shaded.close = function () {
58973 shaded.transition().duration(200).style('opacity', 0).remove();
58974 modal.transition().duration(200).style('top', '0px');
58975 select(document).call(keybinding.unbind);
58978 var modal = shaded.append('div').attr('class', 'modal fillL');
58979 modal.append('input').attr('class', 'keytrap keytrap-first').on('focus.keytrap', moveFocusToLast);
58982 shaded.on('click.remove-modal', function (d3_event) {
58983 if (d3_event.target === _this) {
58987 modal.append('button').attr('class', 'close').on('click', shaded.close).call(svgIcon('#iD-icon-close'));
58988 keybinding.on('⌫', shaded.close).on('⎋', shaded.close);
58989 select(document).call(keybinding);
58992 modal.append('div').attr('class', 'content');
58993 modal.append('input').attr('class', 'keytrap keytrap-last').on('focus.keytrap', moveFocusToFirst);
58996 shaded.transition().style('opacity', 1);
58998 shaded.style('opacity', 1);
59003 function moveFocusToFirst() {
59004 var node = modal // there are additional rules about what's focusable, but this suits our purposes
59005 .select('a, button, input:not(.keytrap), select, textarea').node();
59010 select(this).node().blur();
59014 function moveFocusToLast() {
59015 var nodes = modal.selectAll('a, button, input:not(.keytrap), select, textarea').nodes();
59017 if (nodes.length) {
59018 nodes[nodes.length - 1].focus();
59020 select(this).node().blur();
59025 function uiLoading(context) {
59026 var _modalSelection = select(null);
59029 var _blocking = false;
59031 var loading = function loading(selection) {
59032 _modalSelection = uiModal(selection, _blocking);
59034 var loadertext = _modalSelection.select('.content').classed('loading-modal', true).append('div').attr('class', 'modal-section fillL');
59036 loadertext.append('img').attr('class', 'loader').attr('src', context.imagePath('loader-white.gif'));
59037 loadertext.append('h3').html(_message);
59039 _modalSelection.select('button.close').attr('class', 'hide');
59044 loading.message = function (val) {
59045 if (!arguments.length) return _message;
59050 loading.blocking = function (val) {
59051 if (!arguments.length) return _blocking;
59056 loading.close = function () {
59057 _modalSelection.remove();
59060 loading.isShown = function () {
59061 return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
59067 function coreHistory(context) {
59068 var dispatch$1 = dispatch('reset', 'change', 'merge', 'restore', 'undone', 'redone');
59070 var _lock = utilSessionMutex('lock'); // restorable if iD not open in another window/tab and a saved history exists in localStorage
59073 var _hasUnresolvedRestorableChanges = _lock.lock() && !!corePreferences(getKey('saved_history'));
59075 var duration = 150;
59076 var _imageryUsed = [];
59077 var _photoOverlaysUsed = [];
59078 var _checkpoints = {};
59086 var _tree; // internal _act, accepts list of actions and eased time
59089 function _act(actions, t) {
59090 actions = Array.prototype.slice.call(actions);
59093 if (typeof actions[actions.length - 1] !== 'function') {
59094 annotation = actions.pop();
59097 var graph = _stack[_index].graph;
59099 for (var i = 0; i < actions.length; i++) {
59100 graph = actions[i](graph, t);
59105 annotation: annotation,
59106 imageryUsed: _imageryUsed,
59107 photoOverlaysUsed: _photoOverlaysUsed,
59108 transform: context.projection.transform(),
59109 selectedIDs: context.selectedIDs()
59111 } // internal _perform with eased time
59114 function _perform(args, t) {
59115 var previous = _stack[_index].graph;
59116 _stack = _stack.slice(0, _index + 1);
59118 var actionResult = _act(args, t);
59120 _stack.push(actionResult);
59123 return change(previous);
59124 } // internal _replace with eased time
59127 function _replace(args, t) {
59128 var previous = _stack[_index].graph; // assert(_index == _stack.length - 1)
59130 var actionResult = _act(args, t);
59132 _stack[_index] = actionResult;
59133 return change(previous);
59134 } // internal _overwrite with eased time
59137 function _overwrite(args, t) {
59138 var previous = _stack[_index].graph;
59146 _stack = _stack.slice(0, _index + 1);
59148 var actionResult = _act(args, t);
59150 _stack.push(actionResult);
59153 return change(previous);
59154 } // determine difference and dispatch a change event
59157 function change(previous) {
59158 var difference = coreDifference(previous, history.graph());
59160 if (!_pausedGraph) {
59161 dispatch$1.call('change', this, difference);
59165 } // iD uses namespaced keys so multiple installations do not conflict
59168 function getKey(n) {
59169 return 'iD_' + window.location.origin + '_' + n;
59173 graph: function graph() {
59174 return _stack[_index].graph;
59176 tree: function tree() {
59179 base: function base() {
59180 return _stack[0].graph;
59182 merge: function merge(entities
59185 var stack = _stack.map(function (state) {
59186 return state.graph;
59189 _stack[0].graph.rebase(entities, stack, false);
59191 _tree.rebase(entities, false);
59193 dispatch$1.call('merge', this, entities);
59195 perform: function perform() {
59196 // complete any transition already in progress
59197 select(document).interrupt('history.perform');
59198 var transitionable = false;
59199 var action0 = arguments[0];
59201 if (arguments.length === 1 || arguments.length === 2 && typeof arguments[1] !== 'function') {
59202 transitionable = !!action0.transitionable;
59205 if (transitionable) {
59206 var origArguments = arguments;
59207 select(document).transition('history.perform').duration(duration).ease(linear$1).tween('history.tween', function () {
59208 return function (t) {
59209 if (t < 1) _overwrite([action0], t);
59211 }).on('start', function () {
59212 _perform([action0], 0);
59213 }).on('end interrupt', function () {
59214 _overwrite(origArguments, 1);
59217 return _perform(arguments);
59220 replace: function replace() {
59221 select(document).interrupt('history.perform');
59222 return _replace(arguments, 1);
59224 // Same as calling pop and then perform
59225 overwrite: function overwrite() {
59226 select(document).interrupt('history.perform');
59227 return _overwrite(arguments, 1);
59229 pop: function pop(n) {
59230 select(document).interrupt('history.perform');
59231 var previous = _stack[_index].graph;
59233 if (isNaN(+n) || +n < 0) {
59237 while (n-- > 0 && _index > 0) {
59243 return change(previous);
59245 // Back to the previous annotated state or _index = 0.
59246 undo: function undo() {
59247 select(document).interrupt('history.perform');
59248 var previousStack = _stack[_index];
59249 var previous = previousStack.graph;
59251 while (_index > 0) {
59253 if (_stack[_index].annotation) break;
59256 dispatch$1.call('undone', this, _stack[_index], previousStack);
59257 return change(previous);
59259 // Forward to the next annotated state.
59260 redo: function redo() {
59261 select(document).interrupt('history.perform');
59262 var previousStack = _stack[_index];
59263 var previous = previousStack.graph;
59264 var tryIndex = _index;
59266 while (tryIndex < _stack.length - 1) {
59269 if (_stack[tryIndex].annotation) {
59271 dispatch$1.call('redone', this, _stack[_index], previousStack);
59276 return change(previous);
59278 pauseChangeDispatch: function pauseChangeDispatch() {
59279 if (!_pausedGraph) {
59280 _pausedGraph = _stack[_index].graph;
59283 resumeChangeDispatch: function resumeChangeDispatch() {
59284 if (_pausedGraph) {
59285 var previous = _pausedGraph;
59286 _pausedGraph = null;
59287 return change(previous);
59290 undoAnnotation: function undoAnnotation() {
59294 if (_stack[i].annotation) return _stack[i].annotation;
59298 redoAnnotation: function redoAnnotation() {
59299 var i = _index + 1;
59301 while (i <= _stack.length - 1) {
59302 if (_stack[i].annotation) return _stack[i].annotation;
59306 // Returns the entities from the active graph with bounding boxes
59307 // overlapping the given `extent`.
59308 intersects: function intersects(extent) {
59309 return _tree.intersects(extent, _stack[_index].graph);
59311 difference: function difference() {
59312 var base = _stack[0].graph;
59313 var head = _stack[_index].graph;
59314 return coreDifference(base, head);
59316 changes: function changes(action) {
59317 var base = _stack[0].graph;
59318 var head = _stack[_index].graph;
59321 head = action(head);
59324 var difference = coreDifference(base, head);
59326 modified: difference.modified(),
59327 created: difference.created(),
59328 deleted: difference.deleted()
59331 hasChanges: function hasChanges() {
59332 return this.difference().length() > 0;
59334 imageryUsed: function imageryUsed(sources) {
59336 _imageryUsed = sources;
59341 _stack.slice(1, _index + 1).forEach(function (state) {
59342 state.imageryUsed.forEach(function (source) {
59343 if (source !== 'Custom') {
59349 return Array.from(s);
59352 photoOverlaysUsed: function photoOverlaysUsed(sources) {
59354 _photoOverlaysUsed = sources;
59359 _stack.slice(1, _index + 1).forEach(function (state) {
59360 if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
59361 state.photoOverlaysUsed.forEach(function (photoOverlay) {
59362 s.add(photoOverlay);
59367 return Array.from(s);
59370 // save the current history state
59371 checkpoint: function checkpoint(key) {
59372 _checkpoints[key] = {
59378 // restore history state to a given checkpoint or reset completely
59379 reset: function reset(key) {
59380 if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
59381 _stack = _checkpoints[key].stack;
59382 _index = _checkpoints[key].index;
59388 _tree = coreTree(_stack[0].graph);
59392 dispatch$1.call('reset');
59393 dispatch$1.call('change');
59396 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
59399 // 1. Start the walkthrough.
59400 // 2. Get to a "free editing" tutorial step
59401 // 3. Make your edits to the walkthrough map
59402 // 4. In your browser dev console run:
59403 // `id.history().toIntroGraph()`
59404 // 5. This outputs stringified JSON to the browser console
59405 // 6. Copy it to `data/intro_graph.json` and prettify it in your code editor
59406 toIntroGraph: function toIntroGraph() {
59413 var graph = this.graph();
59414 var baseEntities = {}; // clone base entities..
59416 Object.values(graph.base().entities).forEach(function (entity) {
59417 var copy = copyIntroEntity(entity);
59418 baseEntities[copy.id] = copy;
59419 }); // replace base entities with head entities..
59421 Object.keys(graph.entities).forEach(function (id) {
59422 var entity = graph.entities[id];
59425 var copy = copyIntroEntity(entity);
59426 baseEntities[copy.id] = copy;
59428 delete baseEntities[id];
59430 }); // swap temporary for permanent ids..
59432 Object.values(baseEntities).forEach(function (entity) {
59433 if (Array.isArray(entity.nodes)) {
59434 entity.nodes = entity.nodes.map(function (node) {
59435 return permIDs[node] || node;
59439 if (Array.isArray(entity.members)) {
59440 entity.members = entity.members.map(function (member) {
59441 member.id = permIDs[member.id] || member.id;
59446 return JSON.stringify({
59447 dataIntroGraph: baseEntities
59450 function copyIntroEntity(source) {
59451 var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']); // Note: the copy is no longer an osmEntity, so it might not have `tags`
59453 if (copy.tags && !Object.keys(copy.tags)) {
59457 if (Array.isArray(copy.loc)) {
59458 copy.loc[0] = +copy.loc[0].toFixed(6);
59459 copy.loc[1] = +copy.loc[1].toFixed(6);
59462 var match = source.id.match(/([nrw])-\d*/); // temporary id
59464 if (match !== null) {
59465 var nrw = match[1];
59469 permID = nrw + ++nextID[nrw];
59470 } while (baseEntities.hasOwnProperty(permID));
59472 copy.id = permIDs[source.id] = permID;
59478 toJSON: function toJSON() {
59479 if (!this.hasChanges()) return;
59480 var allEntities = {};
59481 var baseEntities = {};
59482 var base = _stack[0];
59484 var s = _stack.map(function (i) {
59487 Object.keys(i.graph.entities).forEach(function (id) {
59488 var entity = i.graph.entities[id];
59491 var key = osmEntity.key(entity);
59492 allEntities[key] = entity;
59493 modified.push(key);
59496 } // make sure that the originals of changed or deleted entities get merged
59497 // into the base of the _stack after restoring the data from JSON.
59500 if (id in base.graph.entities) {
59501 baseEntities[id] = base.graph.entities[id];
59504 if (entity && entity.nodes) {
59505 // get originals of pre-existing child nodes
59506 entity.nodes.forEach(function (nodeID) {
59507 if (nodeID in base.graph.entities) {
59508 baseEntities[nodeID] = base.graph.entities[nodeID];
59511 } // get originals of parent entities too
59514 var baseParents = base.graph._parentWays[id];
59517 baseParents.forEach(function (parentID) {
59518 if (parentID in base.graph.entities) {
59519 baseEntities[parentID] = base.graph.entities[parentID];
59525 if (modified.length) x.modified = modified;
59526 if (deleted.length) x.deleted = deleted;
59527 if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
59528 if (i.photoOverlaysUsed) x.photoOverlaysUsed = i.photoOverlaysUsed;
59529 if (i.annotation) x.annotation = i.annotation;
59530 if (i.transform) x.transform = i.transform;
59531 if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
59535 return JSON.stringify({
59537 entities: Object.values(allEntities),
59538 baseEntities: Object.values(baseEntities),
59540 nextIDs: osmEntity.id.next,
59542 // note the time the changes were saved
59543 timestamp: new Date().getTime()
59546 fromJSON: function fromJSON(json, loadChildNodes) {
59547 var h = JSON.parse(json);
59548 var loadComplete = true;
59549 osmEntity.id.next = h.nextIDs;
59552 if (h.version === 2 || h.version === 3) {
59553 var allEntities = {};
59554 h.entities.forEach(function (entity) {
59555 allEntities[osmEntity.key(entity)] = osmEntity(entity);
59558 if (h.version === 3) {
59559 // This merges originals for changed entities into the base of
59560 // the _stack even if the current _stack doesn't have them (for
59561 // example when iD has been restarted in a different region)
59562 var baseEntities = h.baseEntities.map(function (d) {
59563 return osmEntity(d);
59566 var stack = _stack.map(function (state) {
59567 return state.graph;
59570 _stack[0].graph.rebase(baseEntities, stack, true);
59572 _tree.rebase(baseEntities, true); // When we restore a modified way, we also need to fetch any missing
59573 // childnodes that would normally have been downloaded with it.. #2142
59576 if (loadChildNodes) {
59577 var osm = context.connection();
59578 var baseWays = baseEntities.filter(function (e) {
59579 return e.type === 'way';
59581 var nodeIDs = baseWays.reduce(function (acc, way) {
59582 return utilArrayUnion(acc, way.nodes);
59584 var missing = nodeIDs.filter(function (n) {
59585 return !_stack[0].graph.hasEntity(n);
59588 if (missing.length && osm) {
59589 loadComplete = false;
59590 context.map().redrawEnable(false);
59591 var loading = uiLoading(context).blocking(true);
59592 context.container().call(loading);
59594 var childNodesLoaded = function childNodesLoaded(err, result) {
59596 var visibleGroups = utilArrayGroupBy(result.data, 'visible');
59597 var visibles = visibleGroups["true"] || []; // alive nodes
59599 var invisibles = visibleGroups["false"] || []; // deleted nodes
59601 if (visibles.length) {
59602 var visibleIDs = visibles.map(function (entity) {
59606 var stack = _stack.map(function (state) {
59607 return state.graph;
59610 missing = utilArrayDifference(missing, visibleIDs);
59612 _stack[0].graph.rebase(visibles, stack, true);
59614 _tree.rebase(visibles, true);
59615 } // fetch older versions of nodes that were deleted..
59618 invisibles.forEach(function (entity) {
59619 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
59623 if (err || !missing.length) {
59625 context.map().redrawEnable(true);
59626 dispatch$1.call('change');
59627 dispatch$1.call('restore', this);
59631 osm.loadMultiple(missing, childNodesLoaded);
59636 _stack = h.stack.map(function (d) {
59641 d.modified.forEach(function (key) {
59642 entity = allEntities[key];
59643 entities[entity.id] = entity;
59648 d.deleted.forEach(function (id) {
59649 entities[id] = undefined;
59654 graph: coreGraph(_stack[0].graph).load(entities),
59655 annotation: d.annotation,
59656 imageryUsed: d.imageryUsed,
59657 photoOverlaysUsed: d.photoOverlaysUsed,
59658 transform: d.transform,
59659 selectedIDs: d.selectedIDs
59663 // original version
59664 _stack = h.stack.map(function (d) {
59667 for (var i in d.entities) {
59668 var entity = d.entities[i];
59669 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
59672 d.graph = coreGraph(_stack[0].graph).load(entities);
59677 var transform = _stack[_index].transform;
59680 context.map().transformEase(transform, 0); // 0 = immediate, no easing
59683 if (loadComplete) {
59684 dispatch$1.call('change');
59685 dispatch$1.call('restore', this);
59690 lock: function lock() {
59691 return _lock.lock();
59693 unlock: function unlock() {
59696 save: function save() {
59697 if (_lock.locked() && // don't overwrite existing, unresolved changes
59698 !_hasUnresolvedRestorableChanges) {
59699 corePreferences(getKey('saved_history'), history.toJSON() || null);
59704 // delete the history version saved in localStorage
59705 clearSaved: function clearSaved() {
59706 context.debouncedSave.cancel();
59708 if (_lock.locked()) {
59709 _hasUnresolvedRestorableChanges = false;
59710 corePreferences(getKey('saved_history'), null); // clear the changeset metadata associated with the saved history
59712 corePreferences('comment', null);
59713 corePreferences('hashtags', null);
59714 corePreferences('source', null);
59719 savedHistoryJSON: function savedHistoryJSON() {
59720 return corePreferences(getKey('saved_history'));
59722 hasRestorableChanges: function hasRestorableChanges() {
59723 return _hasUnresolvedRestorableChanges;
59725 // load history from a version stored in localStorage
59726 restore: function restore() {
59727 if (_lock.locked()) {
59728 _hasUnresolvedRestorableChanges = false;
59729 var json = this.savedHistoryJSON();
59730 if (json) history.fromJSON(json, true);
59736 return utilRebind(history, dispatch$1, 'on');
59740 * Look for roads that can be connected to other roads with a short extension
59743 function validationAlmostJunction(context) {
59744 var type = 'almost_junction';
59745 var EXTEND_TH_METERS = 5;
59746 var WELD_TH_METERS = 0.75; // Comes from considering bounding case of parallel ways
59748 var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS; // Comes from considering bounding case of perpendicular ways
59750 var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
59752 function isHighway(entity) {
59753 return entity.type === 'way' && osmRoutableHighwayTagValues[entity.tags.highway];
59756 function isTaggedAsNotContinuing(node) {
59757 return node.tags.noexit === 'yes' || node.tags.amenity === 'parking_entrance' || node.tags.entrance && node.tags.entrance !== 'no';
59760 var validation = function checkAlmostJunction(entity, graph) {
59761 if (!isHighway(entity)) return [];
59762 if (entity.isDegenerate()) return [];
59763 var tree = context.history().tree();
59764 var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
59766 extendableNodeInfos.forEach(function (extendableNodeInfo) {
59767 issues.push(new validationIssue({
59769 subtype: 'highway-highway',
59770 severity: 'warning',
59771 message: function message(context) {
59772 var entity1 = context.hasEntity(this.entityIds[0]);
59774 if (this.entityIds[0] === this.entityIds[2]) {
59775 return entity1 ? _t.html('issues.almost_junction.self.message', {
59776 feature: utilDisplayLabel(entity1, context.graph())
59779 var entity2 = context.hasEntity(this.entityIds[2]);
59780 return entity1 && entity2 ? _t.html('issues.almost_junction.message', {
59781 feature: utilDisplayLabel(entity1, context.graph()),
59782 feature2: utilDisplayLabel(entity2, context.graph())
59786 reference: showReference,
59787 entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid],
59788 loc: extendableNodeInfo.node.loc,
59789 hash: JSON.stringify(extendableNodeInfo.node.loc),
59791 midId: extendableNodeInfo.mid.id,
59792 edge: extendableNodeInfo.edge,
59793 cross_loc: extendableNodeInfo.cross_loc
59795 dynamicFixes: makeFixes
59800 function makeFixes(context) {
59801 var fixes = [new validationIssueFix({
59802 icon: 'iD-icon-abutment',
59803 title: _t.html('issues.fix.connect_features.title'),
59804 onClick: function onClick(context) {
59805 var annotation = _t('issues.fix.connect_almost_junction.annotation');
59807 var _this$issue$entityIds = _slicedToArray(this.issue.entityIds, 3),
59808 endNodeId = _this$issue$entityIds[1],
59809 crossWayId = _this$issue$entityIds[2];
59811 var midNode = context.entity(this.issue.data.midId);
59812 var endNode = context.entity(endNodeId);
59813 var crossWay = context.entity(crossWayId); // When endpoints are close, just join if resulting small change in angle (#7201)
59815 var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
59817 if (nearEndNodes.length > 0) {
59818 var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
59821 context.perform(actionMergeNodes([collinear.id, endNode.id], collinear.loc), annotation);
59826 var targetEdge = this.issue.data.edge;
59827 var crossLoc = this.issue.data.cross_loc;
59828 var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
59829 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc); // already a point nearby, just connect to that
59831 if (closestNodeInfo.distance < WELD_TH_METERS) {
59832 context.perform(actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc), annotation); // else add the end node to the edge way
59834 context.perform(actionAddMidpoint({
59837 }, endNode), annotation);
59841 var node = context.hasEntity(this.entityIds[1]);
59843 if (node && !node.hasInterestingTags()) {
59844 // node has no descriptive tags, suggest noexit fix
59845 fixes.push(new validationIssueFix({
59846 icon: 'maki-barrier',
59847 title: _t.html('issues.fix.tag_as_disconnected.title'),
59848 onClick: function onClick(context) {
59849 var nodeID = this.issue.entityIds[1];
59850 var tags = Object.assign({}, context.entity(nodeID).tags);
59851 tags.noexit = 'yes';
59852 context.perform(actionChangeTags(nodeID, tags), _t('issues.fix.tag_as_disconnected.annotation'));
59860 function showReference(selection) {
59861 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.almost_junction.highway-highway.reference'));
59864 function isExtendableCandidate(node, way) {
59865 // can not accurately test vertices on tiles not downloaded from osm - #5938
59866 var osm = services.osm;
59868 if (osm && !osm.isDataLoaded(node.loc)) {
59872 if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
59876 var occurrences = 0;
59878 for (var index in way.nodes) {
59879 if (way.nodes[index] === node.id) {
59882 if (occurrences > 1) {
59891 function findConnectableEndNodesByExtension(way) {
59893 if (way.isClosed()) return results;
59895 var indices = [0, way.nodes.length - 1];
59896 indices.forEach(function (nodeIndex) {
59897 var nodeID = way.nodes[nodeIndex];
59898 var node = graph.entity(nodeID);
59899 if (!isExtendableCandidate(node, way)) return;
59900 var connectionInfo = canConnectByExtend(way, nodeIndex);
59901 if (!connectionInfo) return;
59902 testNodes = graph.childNodes(way).slice(); // shallow copy
59904 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc); // don't flag issue if connecting the ways would cause self-intersection
59906 if (geoHasSelfIntersections(testNodes, nodeID)) return;
59907 results.push(connectionInfo);
59912 function findNearbyEndNodes(node, way) {
59913 return [way.nodes[0], way.nodes[way.nodes.length - 1]].map(function (d) {
59914 return graph.entity(d);
59915 }).filter(function (d) {
59916 // Node cannot be near to itself, but other endnode of same way could be
59917 return d.id !== node.id && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
59921 function findSmallJoinAngle(midNode, tipNode, endNodes) {
59922 // Both nodes could be close, so want to join whichever is closest to collinear
59924 var minAngle = Infinity; // Checks midNode -> tipNode -> endNode for collinearity
59926 endNodes.forEach(function (endNode) {
59927 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
59928 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
59929 var diff = Math.max(a1, a2) - Math.min(a1, a2);
59931 if (diff < minAngle) {
59936 /* Threshold set by considering right angle triangle
59937 based on node joining threshold and extension distance */
59939 if (minAngle <= SIG_ANGLE_TH) return joinTo;
59943 function hasTag(tags, key) {
59944 return tags[key] !== undefined && tags[key] !== 'no';
59947 function canConnectWays(way, way2) {
59948 // allow self-connections
59949 if (way.id === way2.id) return true; // if one is bridge or tunnel, both must be bridge or tunnel
59951 if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) && !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) return false;
59952 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
59954 var layer1 = way.tags.layer || '0',
59955 layer2 = way2.tags.layer || '0';
59956 if (layer1 !== layer2) return false;
59957 var level1 = way.tags.level || '0',
59958 level2 = way2.tags.level || '0';
59959 if (level1 !== level2) return false;
59963 function canConnectByExtend(way, endNodeIdx) {
59964 var tipNid = way.nodes[endNodeIdx]; // the 'tip' node for extension point
59966 var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2]; // the other node of the edge
59968 var tipNode = graph.entity(tipNid);
59969 var midNode = graph.entity(midNid);
59970 var lon = tipNode.loc[0];
59971 var lat = tipNode.loc[1];
59972 var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
59973 var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
59974 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
59976 var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
59977 var t = EXTEND_TH_METERS / edgeLen + 1.0;
59978 var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t); // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
59980 var segmentInfos = tree.waySegments(queryExtent, graph);
59982 for (var i = 0; i < segmentInfos.length; i++) {
59983 var segmentInfo = segmentInfos[i];
59984 var way2 = graph.entity(segmentInfo.wayId);
59985 if (!isHighway(way2)) continue;
59986 if (!canConnectWays(way, way2)) continue;
59987 var nAid = segmentInfo.nodes[0],
59988 nBid = segmentInfo.nodes[1];
59989 if (nAid === tipNid || nBid === tipNid) continue;
59990 var nA = graph.entity(nAid),
59991 nB = graph.entity(nBid);
59992 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
59999 edge: [nA.id, nB.id],
60000 cross_loc: crossLoc
60009 validation.type = type;
60013 function validationCloseNodes(context) {
60014 var type = 'close_nodes';
60015 var pointThresholdMeters = 0.2;
60017 var validation = function validation(entity, graph) {
60018 if (entity.type === 'node') {
60019 return getIssuesForNode(entity);
60020 } else if (entity.type === 'way') {
60021 return getIssuesForWay(entity);
60026 function getIssuesForNode(node) {
60027 var parentWays = graph.parentWays(node);
60029 if (parentWays.length) {
60030 return getIssuesForVertex(node, parentWays);
60032 return getIssuesForDetachedPoint(node);
60036 function wayTypeFor(way) {
60037 if (way.tags.boundary && way.tags.boundary !== 'no') return 'boundary';
60038 if (way.tags.indoor && way.tags.indoor !== 'no') return 'indoor';
60039 if (way.tags.building && way.tags.building !== 'no' || way.tags['building:part'] && way.tags['building:part'] !== 'no') return 'building';
60040 if (osmPathHighwayTagValues[way.tags.highway]) return 'path';
60041 var parentRelations = graph.parentRelations(way);
60043 for (var i in parentRelations) {
60044 var relation = parentRelations[i];
60045 if (relation.tags.type === 'boundary') return 'boundary';
60047 if (relation.isMultipolygon()) {
60048 if (relation.tags.indoor && relation.tags.indoor !== 'no') return 'indoor';
60049 if (relation.tags.building && relation.tags.building !== 'no' || relation.tags['building:part'] && relation.tags['building:part'] !== 'no') return 'building';
60056 function shouldCheckWay(way) {
60057 // don't flag issues where merging would create degenerate ways
60058 if (way.nodes.length <= 2 || way.isClosed() && way.nodes.length <= 4) return false;
60059 var bbox = way.extent(graph).bbox();
60060 var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]); // don't flag close nodes in very small ways
60062 if (hypotenuseMeters < 1.5) return false;
60066 function getIssuesForWay(way) {
60067 if (!shouldCheckWay(way)) return [];
60069 nodes = graph.childNodes(way);
60071 for (var i = 0; i < nodes.length - 1; i++) {
60072 var node1 = nodes[i];
60073 var node2 = nodes[i + 1];
60074 var issue = getWayIssueIfAny(node1, node2, way);
60075 if (issue) issues.push(issue);
60081 function getIssuesForVertex(node, parentWays) {
60084 function checkForCloseness(node1, node2, way) {
60085 var issue = getWayIssueIfAny(node1, node2, way);
60086 if (issue) issues.push(issue);
60089 for (var i = 0; i < parentWays.length; i++) {
60090 var parentWay = parentWays[i];
60091 if (!shouldCheckWay(parentWay)) continue;
60092 var lastIndex = parentWay.nodes.length - 1;
60094 for (var j = 0; j < parentWay.nodes.length; j++) {
60096 if (parentWay.nodes[j - 1] === node.id) {
60097 checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
60101 if (j !== lastIndex) {
60102 if (parentWay.nodes[j + 1] === node.id) {
60103 checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
60112 function thresholdMetersForWay(way) {
60113 if (!shouldCheckWay(way)) return 0;
60114 var wayType = wayTypeFor(way); // don't flag boundaries since they might be highly detailed and can't be easily verified
60116 if (wayType === 'boundary') return 0; // expect some features to be mapped with higher levels of detail
60118 if (wayType === 'indoor') return 0.01;
60119 if (wayType === 'building') return 0.05;
60120 if (wayType === 'path') return 0.1;
60124 function getIssuesForDetachedPoint(node) {
60126 var lon = node.loc[0];
60127 var lat = node.loc[1];
60128 var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
60129 var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
60130 var queryExtent = geoExtent([[lon - lon_range, lat - lat_range], [lon + lon_range, lat + lat_range]]);
60131 var intersected = context.history().tree().intersects(queryExtent, graph);
60133 for (var j = 0; j < intersected.length; j++) {
60134 var nearby = intersected[j];
60135 if (nearby.id === node.id) continue;
60136 if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') continue;
60138 if (nearby.loc === node.loc || geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
60139 // allow very close points if tags indicate the z-axis might vary
60143 'addr:housenumber': true,
60146 var zAxisDifferentiates = false;
60148 for (var key in zAxisKeys) {
60149 var nodeValue = node.tags[key] || '0';
60150 var nearbyValue = nearby.tags[key] || '0';
60152 if (nodeValue !== nearbyValue) {
60153 zAxisDifferentiates = true;
60158 if (zAxisDifferentiates) continue;
60159 issues.push(new validationIssue({
60161 subtype: 'detached',
60162 severity: 'warning',
60163 message: function message(context) {
60164 var entity = context.hasEntity(this.entityIds[0]),
60165 entity2 = context.hasEntity(this.entityIds[1]);
60166 return entity && entity2 ? _t.html('issues.close_nodes.detached.message', {
60167 feature: utilDisplayLabel(entity, context.graph()),
60168 feature2: utilDisplayLabel(entity2, context.graph())
60171 reference: showReference,
60172 entityIds: [node.id, nearby.id],
60173 dynamicFixes: function dynamicFixes() {
60174 return [new validationIssueFix({
60175 icon: 'iD-operation-disconnect',
60176 title: _t.html('issues.fix.move_points_apart.title')
60177 }), new validationIssueFix({
60178 icon: 'iD-icon-layers',
60179 title: _t.html('issues.fix.use_different_layers_or_levels.title')
60188 function showReference(selection) {
60189 var referenceText = _t('issues.close_nodes.detached.reference');
60190 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
60194 function getWayIssueIfAny(node1, node2, way) {
60195 if (node1.id === node2.id || node1.hasInterestingTags() && node2.hasInterestingTags()) {
60199 if (node1.loc !== node2.loc) {
60200 var parentWays1 = graph.parentWays(node1);
60201 var parentWays2 = new Set(graph.parentWays(node2));
60202 var sharedWays = parentWays1.filter(function (parentWay) {
60203 return parentWays2.has(parentWay);
60205 var thresholds = sharedWays.map(function (parentWay) {
60206 return thresholdMetersForWay(parentWay);
60208 var threshold = Math.min.apply(Math, _toConsumableArray(thresholds));
60209 var distance = geoSphericalDistance(node1.loc, node2.loc);
60210 if (distance > threshold) return null;
60213 return new validationIssue({
60215 subtype: 'vertices',
60216 severity: 'warning',
60217 message: function message(context) {
60218 var entity = context.hasEntity(this.entityIds[0]);
60219 return entity ? _t.html('issues.close_nodes.message', {
60220 way: utilDisplayLabel(entity, context.graph())
60223 reference: showReference,
60224 entityIds: [way.id, node1.id, node2.id],
60226 dynamicFixes: function dynamicFixes() {
60227 return [new validationIssueFix({
60228 icon: 'iD-icon-plus',
60229 title: _t.html('issues.fix.merge_points.title'),
60230 onClick: function onClick(context) {
60231 var entityIds = this.issue.entityIds;
60232 var action = actionMergeNodes([entityIds[1], entityIds[2]]);
60233 context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
60235 }), new validationIssueFix({
60236 icon: 'iD-operation-disconnect',
60237 title: _t.html('issues.fix.move_points_apart.title')
60242 function showReference(selection) {
60243 var referenceText = _t('issues.close_nodes.reference');
60244 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(referenceText);
60249 validation.type = type;
60253 function validationCrossingWays(context) {
60254 var type = 'crossing_ways'; // returns the way or its parent relation, whichever has a useful feature type
60256 function getFeatureWithFeatureTypeTagsForWay(way, graph) {
60257 if (getFeatureType(way, graph) === null) {
60258 // if the way doesn't match a feature type, check its parent relations
60259 var parentRels = graph.parentRelations(way);
60261 for (var i = 0; i < parentRels.length; i++) {
60262 var rel = parentRels[i];
60264 if (getFeatureType(rel, graph) !== null) {
60273 function hasTag(tags, key) {
60274 return tags[key] !== undefined && tags[key] !== 'no';
60277 function taggedAsIndoor(tags) {
60278 return hasTag(tags, 'indoor') || hasTag(tags, 'level') || tags.highway === 'corridor';
60281 function allowsBridge(featureType) {
60282 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
60285 function allowsTunnel(featureType) {
60286 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
60290 var ignoredBuildings = {
60297 function getFeatureType(entity, graph) {
60298 var geometry = entity.geometry(graph);
60299 if (geometry !== 'line' && geometry !== 'area') return null;
60300 var tags = entity.tags;
60301 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) return 'building';
60302 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) return 'highway'; // don't check railway or waterway areas
60304 if (geometry !== 'line') return null;
60305 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) return 'railway';
60306 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) return 'waterway';
60310 function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
60311 // assume 0 by default
60312 var level1 = tags1.level || '0';
60313 var level2 = tags2.level || '0';
60315 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
60316 // assume features don't interact if they're indoor on different levels
60318 } // assume 0 by default; don't use way.layer() since we account for structures here
60321 var layer1 = tags1.layer || '0';
60322 var layer2 = tags2.layer || '0';
60324 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
60325 if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) return true;
60326 if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) return true; // crossing bridges must use different layers
60328 if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) return true;
60329 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) return true;else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) return true;
60331 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
60332 if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) return true;
60333 if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) return true; // crossing tunnels must use different layers
60335 if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) return true;
60336 } 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
60339 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') return true;
60340 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') return true;
60342 if (featureType1 === 'building' || featureType2 === 'building') {
60343 // for building crossings, different layers are enough
60344 if (layer1 !== layer2) return true;
60348 } // highway values for which we shouldn't recommend connecting to waterways
60351 var highwaysDisallowingFords = {
60353 motorway_link: true,
60357 primary_link: true,
60359 secondary_link: true
60361 var nonCrossingHighways = {
60365 function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
60366 var featureType1 = getFeatureType(entity1, graph);
60367 var featureType2 = getFeatureType(entity2, graph);
60368 var geometry1 = entity1.geometry(graph);
60369 var geometry2 = entity2.geometry(graph);
60370 var bothLines = geometry1 === 'line' && geometry2 === 'line';
60372 if (featureType1 === featureType2) {
60373 if (featureType1 === 'highway') {
60374 var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
60375 var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
60377 if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
60378 // one feature is a path but not both
60379 var roadFeature = entity1IsPath ? entity2 : entity1;
60381 if (nonCrossingHighways[roadFeature.tags.highway]) {
60382 // don't mark path connections with certain roads as crossings
60386 var pathFeature = entity1IsPath ? entity1 : entity2;
60388 if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
60389 // if the path is a crossing, match the crossing type
60390 return bothLines ? {
60391 highway: 'crossing',
60392 crossing: pathFeature.tags.crossing
60394 } // don't add a `crossing` subtag to ambiguous crossings
60397 return bothLines ? {
60398 highway: 'crossing'
60405 if (featureType1 === 'waterway') return {};
60406 if (featureType1 === 'railway') return {};
60408 var featureTypes = [featureType1, featureType2];
60410 if (featureTypes.indexOf('highway') !== -1) {
60411 if (featureTypes.indexOf('railway') !== -1) {
60412 if (!bothLines) return {};
60413 var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
60415 if (osmPathHighwayTagValues[entity1.tags.highway] || osmPathHighwayTagValues[entity2.tags.highway]) {
60416 // path-tram connections use this tag
60417 if (isTram) return {
60418 railway: 'tram_crossing'
60419 }; // other path-rail connections use this tag
60422 railway: 'crossing'
60425 // path-tram connections use this tag
60426 if (isTram) return {
60427 railway: 'tram_level_crossing'
60428 }; // other road-rail connections use this tag
60431 railway: 'level_crossing'
60436 if (featureTypes.indexOf('waterway') !== -1) {
60437 // do not allow fords on structures
60438 if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) return null;
60439 if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) return null;
60441 if (highwaysDisallowingFords[entity1.tags.highway] || highwaysDisallowingFords[entity2.tags.highway]) {
60442 // do not allow fords on major highways
60446 return bothLines ? {
60456 function findCrossingsByWay(way1, graph, tree) {
60457 var edgeCrossInfos = [];
60458 if (way1.type !== 'way') return edgeCrossInfos;
60459 var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
60460 var way1FeatureType = getFeatureType(taggedFeature1, graph);
60461 if (way1FeatureType === null) return edgeCrossInfos;
60462 var checkedSingleCrossingWays = {}; // declare vars ahead of time to reduce garbage collection
60466 var n1, n2, nA, nB, nAId, nBId;
60467 var segment1, segment2;
60469 var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
60470 var way1Nodes = graph.childNodes(way1);
60471 var comparedWays = {};
60473 for (i = 0; i < way1Nodes.length - 1; i++) {
60475 n2 = way1Nodes[i + 1];
60476 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
60477 // of overlapping ways
60479 segmentInfos = tree.waySegments(extent, graph);
60481 for (j = 0; j < segmentInfos.length; j++) {
60482 segment2Info = segmentInfos[j]; // don't check for self-intersection in this validation
60484 if (segment2Info.wayId === way1.id) continue; // skip if this way was already checked and only one issue is needed
60486 if (checkedSingleCrossingWays[segment2Info.wayId]) continue; // mark this way as checked even if there are no crossings
60488 comparedWays[segment2Info.wayId] = true;
60489 way2 = graph.hasEntity(segment2Info.wayId);
60490 if (!way2) continue;
60491 taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph); // only check crossing highway, waterway, building, and railway
60493 way2FeatureType = getFeatureType(taggedFeature2, graph);
60495 if (way2FeatureType === null || isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
60497 } // create only one issue for building crossings
60500 oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
60501 nAId = segment2Info.nodes[0];
60502 nBId = segment2Info.nodes[1];
60504 if (nAId === n1.id || nAId === n2.id || nBId === n1.id || nBId === n2.id) {
60505 // n1 or n2 is a connection node; skip
60509 nA = graph.hasEntity(nAId);
60511 nB = graph.hasEntity(nBId);
60513 segment1 = [n1.loc, n2.loc];
60514 segment2 = [nA.loc, nB.loc];
60515 var point = geoLineIntersection(segment1, segment2);
60518 edgeCrossInfos.push({
60521 featureType: way1FeatureType,
60522 edge: [n1.id, n2.id]
60525 featureType: way2FeatureType,
60526 edge: [nA.id, nB.id]
60532 checkedSingleCrossingWays[way2.id] = true;
60539 return edgeCrossInfos;
60542 function waysToCheck(entity, graph) {
60543 var featureType = getFeatureType(entity, graph);
60544 if (!featureType) return [];
60546 if (entity.type === 'way') {
60548 } else if (entity.type === 'relation') {
60549 return entity.members.reduce(function (array, member) {
60550 if (member.type === 'way' && ( // only look at geometry ways
60551 !member.role || member.role === 'outer' || member.role === 'inner')) {
60552 var entity = graph.hasEntity(member.id); // don't add duplicates
60554 if (entity && array.indexOf(entity) === -1) {
60555 array.push(entity);
60566 var validation = function checkCrossingWays(entity, graph) {
60567 var tree = context.history().tree();
60568 var ways = waysToCheck(entity, graph);
60569 var issues = []; // declare these here to reduce garbage collection
60571 var wayIndex, crossingIndex, crossings;
60573 for (wayIndex in ways) {
60574 crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
60576 for (crossingIndex in crossings) {
60577 issues.push(createIssue(crossings[crossingIndex], graph));
60584 function createIssue(crossing, graph) {
60585 // use the entities with the tags that define the feature type
60586 crossing.wayInfos.sort(function (way1Info, way2Info) {
60587 var type1 = way1Info.featureType;
60588 var type2 = way2Info.featureType;
60590 if (type1 === type2) {
60591 return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
60592 } else if (type1 === 'waterway') {
60594 } else if (type2 === 'waterway') {
60598 return type1 < type2;
60600 var entities = crossing.wayInfos.map(function (wayInfo) {
60601 return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
60603 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
60604 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
60605 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
60606 var featureType1 = crossing.wayInfos[0].featureType;
60607 var featureType2 = crossing.wayInfos[1].featureType;
60608 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
60609 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') && allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
60610 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') && allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
60611 var subtype = [featureType1, featureType2].sort().join('-');
60612 var crossingTypeID = subtype;
60614 if (isCrossingIndoors) {
60615 crossingTypeID = 'indoor-indoor';
60616 } else if (isCrossingTunnels) {
60617 crossingTypeID = 'tunnel-tunnel';
60618 } else if (isCrossingBridges) {
60619 crossingTypeID = 'bridge-bridge';
60622 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
60623 crossingTypeID += '_connectable';
60626 return new validationIssue({
60629 severity: 'warning',
60630 message: function message(context) {
60631 var graph = context.graph();
60632 var entity1 = graph.hasEntity(this.entityIds[0]),
60633 entity2 = graph.hasEntity(this.entityIds[1]);
60634 return entity1 && entity2 ? _t.html('issues.crossing_ways.message', {
60635 feature: utilDisplayLabel(entity1, graph),
60636 feature2: utilDisplayLabel(entity2, graph)
60639 reference: showReference,
60640 entityIds: entities.map(function (entity) {
60645 featureTypes: featureTypes,
60646 connectionTags: connectionTags
60648 // differentiate based on the loc since two ways can cross multiple times
60649 hash: crossing.crossPoint.toString() + // if the edges change then so does the fix
60650 edges.slice().sort(function (edge1, edge2) {
60651 // order to assure hash is deterministic
60652 return edge1[0] < edge2[0] ? -1 : 1;
60653 }).toString() + // ensure the correct connection tags are added in the fix
60654 JSON.stringify(connectionTags),
60655 loc: crossing.crossPoint,
60656 dynamicFixes: function dynamicFixes(context) {
60657 var mode = context.mode();
60658 if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
60659 var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
60660 var selectedFeatureType = this.data.featureTypes[selectedIndex];
60661 var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
60664 if (connectionTags) {
60665 fixes.push(makeConnectWaysFix(this.data.connectionTags));
60668 if (isCrossingIndoors) {
60669 fixes.push(new validationIssueFix({
60670 icon: 'iD-icon-layers',
60671 title: _t.html('issues.fix.use_different_levels.title')
60673 } else if (isCrossingTunnels || isCrossingBridges || featureType1 === 'building' || featureType2 === 'building') {
60674 fixes.push(makeChangeLayerFix('higher'));
60675 fixes.push(makeChangeLayerFix('lower')); // can only add bridge/tunnel if both features are lines
60676 } else if (context.graph().geometry(this.entityIds[0]) === 'line' && context.graph().geometry(this.entityIds[1]) === 'line') {
60677 // don't recommend adding bridges to waterways since they're uncommon
60678 if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
60679 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
60680 } // don't recommend adding tunnels under waterways since they're uncommon
60683 var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
60685 if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
60686 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
60688 } // repositioning the features is always an option
60691 fixes.push(new validationIssueFix({
60692 icon: 'iD-operation-move',
60693 title: _t.html('issues.fix.reposition_features.title')
60699 function showReference(selection) {
60700 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.crossing_ways.' + crossingTypeID + '.reference'));
60704 function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
60705 return new validationIssueFix({
60707 title: _t.html('issues.fix.' + fixTitleID + '.title'),
60708 onClick: function onClick(context) {
60709 var mode = context.mode();
60710 if (!mode || mode.id !== 'select') return;
60711 var selectedIDs = mode.selectedIDs();
60712 if (selectedIDs.length !== 1) return;
60713 var selectedWayID = selectedIDs[0];
60714 if (!context.hasEntity(selectedWayID)) return;
60715 var resultWayIDs = [selectedWayID];
60716 var edge, crossedEdge, crossedWayID;
60718 if (this.issue.entityIds[0] === selectedWayID) {
60719 edge = this.issue.data.edges[0];
60720 crossedEdge = this.issue.data.edges[1];
60721 crossedWayID = this.issue.entityIds[1];
60723 edge = this.issue.data.edges[1];
60724 crossedEdge = this.issue.data.edges[0];
60725 crossedWayID = this.issue.entityIds[0];
60728 var crossingLoc = this.issue.loc;
60729 var projection = context.projection;
60731 var action = function actionAddStructure(graph) {
60732 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
60733 var crossedWay = graph.hasEntity(crossedWayID); // use the explicit width of the crossed feature as the structure length, if available
60735 var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
60737 if (!structLengthMeters) {
60738 // if no explicit width is set, approximate the width based on the tags
60739 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
60742 if (structLengthMeters) {
60743 if (getFeatureType(crossedWay, graph) === 'railway') {
60744 // bridges over railways are generally much longer than the rail bed itself, compensate
60745 structLengthMeters *= 2;
60748 // should ideally never land here since all rail/water/road tags should have an implied width
60749 structLengthMeters = 8;
60752 var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
60753 var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
60754 var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
60755 if (crossingAngle > Math.PI) crossingAngle -= Math.PI; // lengthen the structure to account for the angle of the crossing
60757 structLengthMeters = structLengthMeters / 2 / Math.sin(crossingAngle) * 2; // add padding since the structure must extend past the edges of the crossed feature
60759 structLengthMeters += 4; // clamp the length to a reasonable range
60761 structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
60763 function geomToProj(geoPoint) {
60764 return [geoLonToMeters(geoPoint[0], geoPoint[1]), geoLatToMeters(geoPoint[1])];
60767 function projToGeom(projPoint) {
60768 var lat = geoMetersToLat(projPoint[1]);
60769 return [geoMetersToLon(projPoint[0], lat), lat];
60772 var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
60773 var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
60774 var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
60775 var projectedCrossingLoc = geomToProj(crossingLoc);
60776 var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) / geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
60778 function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
60779 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
60780 return projToGeom([projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters, projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters]);
60783 var endpointLocGetter1 = function endpointLocGetter1(lengthMeters) {
60784 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
60787 var endpointLocGetter2 = function endpointLocGetter2(lengthMeters) {
60788 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
60789 }; // avoid creating very short edges from splitting too close to another node
60792 var minEdgeLengthMeters = 0.55; // decide where to bound the structure along the way, splitting as necessary
60794 function determineEndpoint(edge, endNode, locGetter) {
60796 var idealLengthMeters = structLengthMeters / 2; // distance between the crossing location and the end of the edge,
60797 // the maximum length of this side of the structure
60799 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
60801 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
60802 // the edge is long enough to insert a new node
60803 // the loc that would result in the full expected length
60804 var idealNodeLoc = locGetter(idealLengthMeters);
60805 newNode = osmNode();
60806 graph = actionAddMidpoint({
60809 }, newNode)(graph);
60812 endNode.parentIntersectionWays(graph).forEach(function (way) {
60813 way.nodes.forEach(function (nodeID) {
60814 if (nodeID === endNode.id) {
60815 if (endNode.id === way.first() && endNode.id !== way.last() || endNode.id === way.last() && endNode.id !== way.first()) {
60824 if (edgeCount >= 3) {
60825 // the end node is a junction, try to leave a segment
60826 // between it and the structure - #7202
60827 var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
60829 if (insetLength > minEdgeLengthMeters) {
60830 var insetNodeLoc = locGetter(insetLength);
60831 newNode = osmNode();
60832 graph = actionAddMidpoint({
60835 }, newNode)(graph);
60838 } // if the edge is too short to subdivide as desired, then
60839 // just bound the structure at the existing end node
60842 if (!newNode) newNode = endNode;
60843 var splitAction = actionSplit([newNode.id]).limitWays(resultWayIDs); // only split selected or created ways
60846 graph = splitAction(graph);
60848 if (splitAction.getCreatedWayIDs().length) {
60849 resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
60855 var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
60856 var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
60857 var structureWay = resultWayIDs.map(function (id) {
60858 return graph.entity(id);
60859 }).find(function (way) {
60860 return way.nodes.indexOf(structEndNode1.id) !== -1 && way.nodes.indexOf(structEndNode2.id) !== -1;
60862 var tags = Object.assign({}, structureWay.tags); // copy tags
60864 if (bridgeOrTunnel === 'bridge') {
60865 tags.bridge = 'yes';
60868 var tunnelValue = 'yes';
60870 if (getFeatureType(structureWay, graph) === 'waterway') {
60871 // use `tunnel=culvert` for waterways by default
60872 tunnelValue = 'culvert';
60875 tags.tunnel = tunnelValue;
60877 } // apply the structure tags to the way
60880 graph = actionChangeTags(structureWay.id, tags)(graph);
60884 context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
60885 context.enter(modeSelect(context, resultWayIDs));
60890 function makeConnectWaysFix(connectionTags) {
60891 var fixTitleID = 'connect_features';
60893 if (connectionTags.ford) {
60894 fixTitleID = 'connect_using_ford';
60897 return new validationIssueFix({
60898 icon: 'iD-icon-crossing',
60899 title: _t.html('issues.fix.' + fixTitleID + '.title'),
60900 onClick: function onClick(context) {
60901 var loc = this.issue.loc;
60902 var connectionTags = this.issue.data.connectionTags;
60903 var edges = this.issue.data.edges;
60904 context.perform(function actionConnectCrossingWays(graph) {
60905 // create the new node for the points
60906 var node = osmNode({
60908 tags: connectionTags
60910 graph = graph.replace(node);
60911 var nodesToMerge = [node.id];
60912 var mergeThresholdInMeters = 0.75;
60913 edges.forEach(function (edge) {
60914 var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
60915 var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc); // if there is already a point nearby, use that
60917 if (closestNodeInfo.distance < mergeThresholdInMeters) {
60918 nodesToMerge.push(closestNodeInfo.node.id); // else add the new node to the way
60920 graph = actionAddMidpoint({
60927 if (nodesToMerge.length > 1) {
60928 // if we're using nearby nodes, merge them with the new node
60929 graph = actionMergeNodes(nodesToMerge, loc)(graph);
60933 }, _t('issues.fix.connect_crossing_features.annotation'));
60938 function makeChangeLayerFix(higherOrLower) {
60939 return new validationIssueFix({
60940 icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
60941 title: _t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'),
60942 onClick: function onClick(context) {
60943 var mode = context.mode();
60944 if (!mode || mode.id !== 'select') return;
60945 var selectedIDs = mode.selectedIDs();
60946 if (selectedIDs.length !== 1) return;
60947 var selectedID = selectedIDs[0];
60948 if (!this.issue.entityIds.some(function (entityId) {
60949 return entityId === selectedID;
60951 var entity = context.hasEntity(selectedID);
60952 if (!entity) return;
60953 var tags = Object.assign({}, entity.tags); // shallow copy
60955 var layer = tags.layer && Number(tags.layer);
60957 if (layer && !isNaN(layer)) {
60958 if (higherOrLower === 'higher') {
60964 if (higherOrLower === 'higher') {
60971 tags.layer = layer.toString();
60972 context.perform(actionChangeTags(entity.id, tags), _t('operations.change_tags.annotation'));
60977 validation.type = type;
60981 function validationDisconnectedWay() {
60982 var type = 'disconnected_way';
60984 function isTaggedAsHighway(entity) {
60985 return osmRoutableHighwayTagValues[entity.tags.highway];
60988 var validation = function checkDisconnectedWay(entity, graph) {
60989 var routingIslandWays = routingIslandForEntity(entity);
60990 if (!routingIslandWays) return [];
60991 return [new validationIssue({
60993 subtype: 'highway',
60994 severity: 'warning',
60995 message: function message(context) {
60996 var entity = this.entityIds.length && context.hasEntity(this.entityIds[0]);
60997 var label = entity && utilDisplayLabel(entity, context.graph());
60998 return _t.html('issues.disconnected_way.routable.message', {
60999 count: this.entityIds.length,
61003 reference: showReference,
61004 entityIds: Array.from(routingIslandWays).map(function (way) {
61007 dynamicFixes: makeFixes
61010 function makeFixes(context) {
61012 var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
61014 if (singleEntity) {
61015 if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
61016 var textDirection = _mainLocalizer.textDirection();
61017 var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
61018 if (startFix) fixes.push(startFix);
61019 var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
61020 if (endFix) fixes.push(endFix);
61023 if (!fixes.length) {
61024 fixes.push(new validationIssueFix({
61025 title: _t.html('issues.fix.connect_feature.title')
61029 fixes.push(new validationIssueFix({
61030 icon: 'iD-operation-delete',
61031 title: _t.html('issues.fix.delete_feature.title'),
61032 entityIds: [singleEntity.id],
61033 onClick: function onClick(context) {
61034 var id = this.issue.entityIds[0];
61035 var operation = operationDelete(context, [id]);
61037 if (!operation.disabled()) {
61043 fixes.push(new validationIssueFix({
61044 title: _t.html('issues.fix.connect_features.title')
61051 function showReference(selection) {
61052 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.disconnected_way.routable.reference'));
61055 function routingIslandForEntity(entity) {
61056 var routingIsland = new Set(); // the interconnected routable features
61058 var waysToCheck = []; // the queue of remaining routable ways to traverse
61060 function queueParentWays(node) {
61061 graph.parentWays(node).forEach(function (parentWay) {
61062 if (!routingIsland.has(parentWay) && // only check each feature once
61063 isRoutableWay(parentWay, false)) {
61064 // only check routable features
61065 routingIsland.add(parentWay);
61066 waysToCheck.push(parentWay);
61071 if (entity.type === 'way' && isRoutableWay(entity, true)) {
61072 routingIsland.add(entity);
61073 waysToCheck.push(entity);
61074 } else if (entity.type === 'node' && isRoutableNode(entity)) {
61075 routingIsland.add(entity);
61076 queueParentWays(entity);
61078 // this feature isn't routable, cannot be a routing island
61082 while (waysToCheck.length) {
61083 var wayToCheck = waysToCheck.pop();
61084 var childNodes = graph.childNodes(wayToCheck);
61086 for (var i in childNodes) {
61087 var vertex = childNodes[i];
61089 if (isConnectedVertex(vertex)) {
61090 // found a link to the wider network, not a routing island
61094 if (isRoutableNode(vertex)) {
61095 routingIsland.add(vertex);
61098 queueParentWays(vertex);
61100 } // no network link found, this is a routing island, return its members
61103 return routingIsland;
61106 function isConnectedVertex(vertex) {
61107 // assume ways overlapping unloaded tiles are connected to the wider road network - #5938
61108 var osm = services.osm;
61109 if (osm && !osm.isDataLoaded(vertex.loc)) return true; // entrances are considered connected
61111 if (vertex.tags.entrance && vertex.tags.entrance !== 'no') return true;
61112 if (vertex.tags.amenity === 'parking_entrance') return true;
61116 function isRoutableNode(node) {
61117 // treat elevators as distinct features in the highway network
61118 if (node.tags.highway === 'elevator') return true;
61122 function isRoutableWay(way, ignoreInnerWays) {
61123 if (isTaggedAsHighway(way) || way.tags.route === 'ferry') return true;
61124 return graph.parentRelations(way).some(function (parentRelation) {
61125 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true;
61126 if (parentRelation.isMultipolygon() && isTaggedAsHighway(parentRelation) && (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) return true;
61131 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
61132 var vertex = graph.hasEntity(vertexID);
61133 if (!vertex || vertex.tags.noexit === 'yes') return null;
61134 var useLeftContinue = whichEnd === 'start' && textDirection === 'ltr' || whichEnd === 'end' && textDirection === 'rtl';
61135 return new validationIssueFix({
61136 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
61137 title: _t.html('issues.fix.continue_from_' + whichEnd + '.title'),
61138 entityIds: [vertexID],
61139 onClick: function onClick(context) {
61140 var wayId = this.issue.entityIds[0];
61141 var way = context.hasEntity(wayId);
61142 var vertexId = this.entityIds[0];
61143 var vertex = context.hasEntity(vertexId);
61144 if (!way || !vertex) return; // make sure the vertex is actually visible and editable
61146 var map = context.map();
61148 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
61149 map.zoomToEase(vertex);
61152 context.enter(modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true));
61158 validation.type = type;
61162 function validationFormatting() {
61163 var type = 'invalid_format';
61165 var validation = function validation(entity) {
61168 function isValidEmail(email) {
61169 // Emails in OSM are going to be official so they should be pretty simple
61170 // Using negated lists to better support all possible unicode characters (#6494)
61171 var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i; // An empty value is also acceptable
61173 return !email || valid_email.test(email);
61176 function isSchemePresent(url) {
61177 var valid_scheme = /^https?:\/\//i;
61178 return (!url || valid_scheme.test(url));
61183 function showReferenceEmail(selection) {
61184 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.invalid_format.email.reference'));
61187 function showReferenceWebsite(selection) {
61188 selection.selectAll('.issue-reference')
61192 .attr('class', 'issue-reference')
61193 .html(t.html('issues.invalid_format.website.reference'));
61195 if (entity.tags.website) {
61196 // Multiple websites are possible
61197 // If ever we support ES6, arrow functions make this nicer
61198 var websites = entity.tags.website
61200 .map(function(s) { return s.trim(); })
61201 .filter(function(x) { return !isSchemePresent(x); });
61202 if (websites.length) {
61203 issues.push(new validationIssue({
61205 subtype: 'website',
61206 severity: 'warning',
61207 message: function(context) {
61208 var entity = context.hasEntity(this.entityIds[0]);
61209 return entity ? t.html('issues.invalid_format.website.message' + this.data,
61210 { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
61212 reference: showReferenceWebsite,
61213 entityIds: [entity.id],
61214 hash: websites.join(),
61215 data: (websites.length > 1) ? '_multi' : ''
61222 if (entity.tags.email) {
61223 // Multiple emails are possible
61224 var emails = entity.tags.email.split(';').map(function (s) {
61226 }).filter(function (x) {
61227 return !isValidEmail(x);
61230 if (emails.length) {
61231 issues.push(new validationIssue({
61234 severity: 'warning',
61235 message: function message(context) {
61236 var entity = context.hasEntity(this.entityIds[0]);
61237 return entity ? _t.html('issues.invalid_format.email.message' + this.data, {
61238 feature: utilDisplayLabel(entity, context.graph()),
61239 email: emails.join(', ')
61242 reference: showReferenceEmail,
61243 entityIds: [entity.id],
61244 hash: emails.join(),
61245 data: emails.length > 1 ? '_multi' : ''
61253 validation.type = type;
61257 function validationHelpRequest(context) {
61258 var type = 'help_request';
61260 var validation = function checkFixmeTag(entity) {
61261 if (!entity.tags.fixme) return []; // don't flag fixmes on features added by the user
61263 if (entity.version === undefined) return [];
61265 if (entity.v !== undefined) {
61266 var baseEntity = context.history().base().hasEntity(entity.id); // don't flag fixmes added by the user on existing features
61268 if (!baseEntity || !baseEntity.tags.fixme) return [];
61271 return [new validationIssue({
61273 subtype: 'fixme_tag',
61274 severity: 'warning',
61275 message: function message(context) {
61276 var entity = context.hasEntity(this.entityIds[0]);
61277 return entity ? _t.html('issues.fixme_tag.message', {
61278 feature: utilDisplayLabel(entity, context.graph())
61281 dynamicFixes: function dynamicFixes() {
61282 return [new validationIssueFix({
61283 title: _t.html('issues.fix.address_the_concern.title')
61286 reference: showReference,
61287 entityIds: [entity.id]
61290 function showReference(selection) {
61291 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.fixme_tag.reference'));
61295 validation.type = type;
61299 function validationImpossibleOneway() {
61300 var type = 'impossible_oneway';
61302 var validation = function checkImpossibleOneway(entity, graph) {
61303 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') return [];
61304 if (entity.isClosed()) return [];
61305 if (!typeForWay(entity)) return [];
61306 if (!isOneway(entity)) return [];
61307 var firstIssues = issuesForNode(entity, entity.first());
61308 var lastIssues = issuesForNode(entity, entity.last());
61309 return firstIssues.concat(lastIssues);
61311 function typeForWay(way) {
61312 if (way.geometry(graph) !== 'line') return null;
61313 if (osmRoutableHighwayTagValues[way.tags.highway]) return 'highway';
61314 if (osmFlowingWaterwayTagValues[way.tags.waterway]) return 'waterway';
61318 function isOneway(way) {
61319 if (way.tags.oneway === 'yes') return true;
61320 if (way.tags.oneway) return false;
61322 for (var key in way.tags) {
61323 if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
61331 function nodeOccursMoreThanOnce(way, nodeID) {
61332 var occurrences = 0;
61334 for (var index in way.nodes) {
61335 if (way.nodes[index] === nodeID) {
61337 if (occurrences > 1) return true;
61344 function isConnectedViaOtherTypes(way, node) {
61345 var wayType = typeForWay(way);
61347 if (wayType === 'highway') {
61348 // entrances are considered connected
61349 if (node.tags.entrance && node.tags.entrance !== 'no') return true;
61350 if (node.tags.amenity === 'parking_entrance') return true;
61351 } else if (wayType === 'waterway') {
61352 if (node.id === way.first()) {
61353 // multiple waterways may start at the same spring
61354 if (node.tags.natural === 'spring') return true;
61356 // multiple waterways may end at the same drain
61357 if (node.tags.manhole === 'drain') return true;
61361 return graph.parentWays(node).some(function (parentWay) {
61362 if (parentWay.id === way.id) return false;
61364 if (wayType === 'highway') {
61365 // allow connections to highway areas
61366 if (parentWay.geometry(graph) === 'area' && osmRoutableHighwayTagValues[parentWay.tags.highway]) return true; // count connections to ferry routes as connected
61368 if (parentWay.tags.route === 'ferry') return true;
61369 return graph.parentRelations(parentWay).some(function (parentRelation) {
61370 if (parentRelation.tags.type === 'route' && parentRelation.tags.route === 'ferry') return true; // allow connections to highway multipolygons
61372 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
61374 } else if (wayType === 'waterway') {
61375 // multiple waterways may start or end at a water body at the same node
61376 if (parentWay.tags.natural === 'water' || parentWay.tags.natural === 'coastline') return true;
61383 function issuesForNode(way, nodeID) {
61384 var isFirst = nodeID === way.first();
61385 var wayType = typeForWay(way); // ignore if this way is self-connected at this node
61387 if (nodeOccursMoreThanOnce(way, nodeID)) return [];
61388 var osm = services.osm;
61389 if (!osm) return [];
61390 var node = graph.hasEntity(nodeID); // ignore if this node or its tile are unloaded
61392 if (!node || !osm.isDataLoaded(node.loc)) return [];
61393 if (isConnectedViaOtherTypes(way, node)) return [];
61394 var attachedWaysOfSameType = graph.parentWays(node).filter(function (parentWay) {
61395 if (parentWay.id === way.id) return false;
61396 return typeForWay(parentWay) === wayType;
61397 }); // assume it's okay for waterways to start or end disconnected for now
61399 if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) return [];
61400 var attachedOneways = attachedWaysOfSameType.filter(function (attachedWay) {
61401 return isOneway(attachedWay);
61402 }); // ignore if the way is connected to some non-oneway features
61404 if (attachedOneways.length < attachedWaysOfSameType.length) return [];
61406 if (attachedOneways.length) {
61407 var connectedEndpointsOkay = attachedOneways.some(function (attachedOneway) {
61408 if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) return true;
61409 if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) return true;
61412 if (connectedEndpointsOkay) return [];
61415 var placement = isFirst ? 'start' : 'end',
61416 messageID = wayType + '.',
61417 referenceID = wayType + '.';
61419 if (wayType === 'waterway') {
61420 messageID += 'connected.' + placement;
61421 referenceID += 'connected';
61423 messageID += placement;
61424 referenceID += placement;
61427 return [new validationIssue({
61430 severity: 'warning',
61431 message: function message(context) {
61432 var entity = context.hasEntity(this.entityIds[0]);
61433 return entity ? _t.html('issues.impossible_oneway.' + messageID + '.message', {
61434 feature: utilDisplayLabel(entity, context.graph())
61437 reference: getReference(referenceID),
61438 entityIds: [way.id, node.id],
61439 dynamicFixes: function dynamicFixes() {
61442 if (attachedOneways.length) {
61443 fixes.push(new validationIssueFix({
61444 icon: 'iD-operation-reverse',
61445 title: _t.html('issues.fix.reverse_feature.title'),
61446 entityIds: [way.id],
61447 onClick: function onClick(context) {
61448 var id = this.issue.entityIds[0];
61449 context.perform(actionReverse(id), _t('operations.reverse.annotation.line', {
61456 if (node.tags.noexit !== 'yes') {
61457 var textDirection = _mainLocalizer.textDirection();
61458 var useLeftContinue = isFirst && textDirection === 'ltr' || !isFirst && textDirection === 'rtl';
61459 fixes.push(new validationIssueFix({
61460 icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
61461 title: _t.html('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
61462 onClick: function onClick(context) {
61463 var entityID = this.issue.entityIds[0];
61464 var vertexID = this.issue.entityIds[1];
61465 var way = context.entity(entityID);
61466 var vertex = context.entity(vertexID);
61467 continueDrawing(way, vertex, context);
61477 function getReference(referenceID) {
61478 return function showReference(selection) {
61479 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.impossible_oneway.' + referenceID + '.reference'));
61485 function continueDrawing(way, vertex, context) {
61486 // make sure the vertex is actually visible and editable
61487 var map = context.map();
61489 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
61490 map.zoomToEase(vertex);
61493 context.enter(modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true));
61496 validation.type = type;
61500 function validationIncompatibleSource() {
61501 var type = 'incompatible_source';
61502 var invalidSources = [{
61505 exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
61508 var validation = function checkIncompatibleSource(entity) {
61509 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
61510 if (!entitySources) return [];
61512 invalidSources.forEach(function (invalidSource) {
61513 var hasInvalidSource = entitySources.some(function (source) {
61514 if (!source.match(new RegExp(invalidSource.regex, 'i'))) return false;
61515 if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) return false;
61518 if (!hasInvalidSource) return;
61519 issues.push(new validationIssue({
61521 severity: 'warning',
61522 message: function message(context) {
61523 var entity = context.hasEntity(this.entityIds[0]);
61524 return entity ? _t.html('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
61525 feature: utilDisplayLabel(entity, context.graph())
61528 reference: getReference(invalidSource.id),
61529 entityIds: [entity.id],
61530 dynamicFixes: function dynamicFixes() {
61531 return [new validationIssueFix({
61532 title: _t.html('issues.fix.remove_proprietary_data.title')
61539 function getReference(id) {
61540 return function showReference(selection) {
61541 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.incompatible_source.' + id + '.reference'));
61546 validation.type = type;
61550 function validationMaprules() {
61551 var type = 'maprules';
61553 var validation = function checkMaprules(entity, graph) {
61554 if (!services.maprules) return [];
61555 var rules = services.maprules.validationRules();
61558 for (var i = 0; i < rules.length; i++) {
61559 var rule = rules[i];
61560 rule.findIssues(entity, graph, issues);
61566 validation.type = type;
61570 function validationMismatchedGeometry() {
61571 var type = 'mismatched_geometry';
61573 function tagSuggestingLineIsArea(entity) {
61574 if (entity.type !== 'way' || entity.isClosed()) return null;
61575 var tagSuggestingArea = entity.tagSuggestingArea();
61577 if (!tagSuggestingArea) {
61581 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
61582 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
61584 if (asLine && asArea && asLine === asArea) {
61585 // these tags also allow lines and making this an area wouldn't matter
61589 return tagSuggestingArea;
61592 function makeConnectEndpointsFixOnClick(way, graph) {
61593 // must have at least three nodes to close this automatically
61594 if (way.nodes.length < 3) return null;
61595 var nodes = graph.childNodes(way),
61597 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length - 1].loc); // if the distance is very small, attempt to merge the endpoints
61599 if (firstToLastDistanceMeters < 0.75) {
61600 testNodes = nodes.slice(); // shallow copy
61603 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
61605 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
61606 return function (context) {
61607 var way = context.entity(this.issue.entityIds[0]);
61608 context.perform(actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length - 1]], nodes[0].loc), _t('issues.fix.connect_endpoints.annotation'));
61611 } // if the points were not merged, attempt to close the way
61614 testNodes = nodes.slice(); // shallow copy
61616 testNodes.push(testNodes[0]); // make sure this will not create a self-intersection
61618 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
61619 return function (context) {
61620 var wayId = this.issue.entityIds[0];
61621 var way = context.entity(wayId);
61622 var nodeId = way.nodes[0];
61623 var index = way.nodes.length;
61624 context.perform(actionAddVertex(wayId, nodeId, index), _t('issues.fix.connect_endpoints.annotation'));
61629 function lineTaggedAsAreaIssue(entity) {
61630 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
61631 if (!tagSuggestingArea) return null;
61632 return new validationIssue({
61634 subtype: 'area_as_line',
61635 severity: 'warning',
61636 message: function message(context) {
61637 var entity = context.hasEntity(this.entityIds[0]);
61638 return entity ? _t.html('issues.tag_suggests_area.message', {
61639 feature: utilDisplayLabel(entity, 'area'),
61641 tags: tagSuggestingArea
61645 reference: showReference,
61646 entityIds: [entity.id],
61647 hash: JSON.stringify(tagSuggestingArea),
61648 dynamicFixes: function dynamicFixes(context) {
61650 var entity = context.entity(this.entityIds[0]);
61651 var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
61652 fixes.push(new validationIssueFix({
61653 title: _t.html('issues.fix.connect_endpoints.title'),
61654 onClick: connectEndsOnClick
61656 fixes.push(new validationIssueFix({
61657 icon: 'iD-operation-delete',
61658 title: _t.html('issues.fix.remove_tag.title'),
61659 onClick: function onClick(context) {
61660 var entityId = this.issue.entityIds[0];
61661 var entity = context.entity(entityId);
61662 var tags = Object.assign({}, entity.tags); // shallow copy
61664 for (var key in tagSuggestingArea) {
61668 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_tag.annotation'));
61675 function showReference(selection) {
61676 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.tag_suggests_area.reference'));
61680 function vertexTaggedAsPointIssue(entity, graph) {
61681 // we only care about nodes
61682 if (entity.type !== 'node') return null; // ignore tagless points
61684 if (Object.keys(entity.tags).length === 0) return null; // address lines are special so just ignore them
61686 if (entity.isOnAddressLine(graph)) return null;
61687 var geometry = entity.geometry(graph);
61688 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
61690 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
61691 return new validationIssue({
61693 subtype: 'vertex_as_point',
61694 severity: 'warning',
61695 message: function message(context) {
61696 var entity = context.hasEntity(this.entityIds[0]);
61697 return entity ? _t.html('issues.vertex_as_point.message', {
61698 feature: utilDisplayLabel(entity, 'vertex')
61701 reference: function showReference(selection) {
61702 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.vertex_as_point.reference'));
61704 entityIds: [entity.id]
61706 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
61707 return new validationIssue({
61709 subtype: 'point_as_vertex',
61710 severity: 'warning',
61711 message: function message(context) {
61712 var entity = context.hasEntity(this.entityIds[0]);
61713 return entity ? _t.html('issues.point_as_vertex.message', {
61714 feature: utilDisplayLabel(entity, 'point')
61717 reference: function showReference(selection) {
61718 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.point_as_vertex.reference'));
61720 entityIds: [entity.id],
61721 dynamicFixes: function dynamicFixes(context) {
61722 var entityId = this.entityIds[0];
61723 var extractOnClick = null;
61725 if (!context.hasHiddenConnections(entityId)) {
61726 extractOnClick = function extractOnClick(context) {
61727 var entityId = this.issue.entityIds[0];
61728 var action = actionExtract(entityId);
61729 context.perform(action, _t('operations.extract.annotation', {
61731 })); // re-enter mode to trigger updates
61733 context.enter(modeSelect(context, [action.getExtractedNodeID()]));
61737 return [new validationIssueFix({
61738 icon: 'iD-operation-extract',
61739 title: _t.html('issues.fix.extract_point.title'),
61740 onClick: extractOnClick
61749 function unclosedMultipolygonPartIssues(entity, graph) {
61750 if (entity.type !== 'relation' || !entity.isMultipolygon() || entity.isDegenerate() || // cannot determine issues for incompletely-downloaded relations
61751 !entity.isComplete(graph)) return null;
61752 var sequences = osmJoinWays(entity.members, graph);
61755 for (var i in sequences) {
61756 var sequence = sequences[i];
61757 if (!sequence.nodes) continue;
61758 var firstNode = sequence.nodes[0];
61759 var lastNode = sequence.nodes[sequence.nodes.length - 1]; // part is closed if the first and last nodes are the same
61761 if (firstNode === lastNode) continue;
61762 var issue = new validationIssue({
61764 subtype: 'unclosed_multipolygon_part',
61765 severity: 'warning',
61766 message: function message(context) {
61767 var entity = context.hasEntity(this.entityIds[0]);
61768 return entity ? _t.html('issues.unclosed_multipolygon_part.message', {
61769 feature: utilDisplayLabel(entity, context.graph())
61772 reference: showReference,
61773 loc: sequence.nodes[0].loc,
61774 entityIds: [entity.id],
61775 hash: sequence.map(function (way) {
61779 issues.push(issue);
61784 function showReference(selection) {
61785 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unclosed_multipolygon_part.reference'));
61789 var validation = function checkMismatchedGeometry(entity, graph) {
61790 var issues = [vertexTaggedAsPointIssue(entity, graph), lineTaggedAsAreaIssue(entity)];
61791 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
61792 return issues.filter(Boolean);
61795 validation.type = type;
61799 function validationMissingRole() {
61800 var type = 'missing_role';
61802 var validation = function checkMissingRole(entity, graph) {
61805 if (entity.type === 'way') {
61806 graph.parentRelations(entity).forEach(function (relation) {
61807 if (!relation.isMultipolygon()) return;
61808 var member = relation.memberById(entity.id);
61810 if (member && isMissingRole(member)) {
61811 issues.push(makeIssue(entity, relation, member));
61814 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
61815 entity.indexedMembers().forEach(function (member) {
61816 var way = graph.hasEntity(member.id);
61818 if (way && isMissingRole(member)) {
61819 issues.push(makeIssue(way, entity, member));
61827 function isMissingRole(member) {
61828 return !member.role || !member.role.trim().length;
61831 function makeIssue(way, relation, member) {
61832 return new validationIssue({
61834 severity: 'warning',
61835 message: function message(context) {
61836 var member = context.hasEntity(this.entityIds[1]),
61837 relation = context.hasEntity(this.entityIds[0]);
61838 return member && relation ? _t.html('issues.missing_role.message', {
61839 member: utilDisplayLabel(member, context.graph()),
61840 relation: utilDisplayLabel(relation, context.graph())
61843 reference: showReference,
61844 entityIds: [relation.id, way.id],
61848 hash: member.index.toString(),
61849 dynamicFixes: function dynamicFixes() {
61850 return [makeAddRoleFix('inner'), makeAddRoleFix('outer'), new validationIssueFix({
61851 icon: 'iD-operation-delete',
61852 title: _t.html('issues.fix.remove_from_relation.title'),
61853 onClick: function onClick(context) {
61854 context.perform(actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index), _t('operations.delete_member.annotation', {
61862 function showReference(selection) {
61863 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.missing_role.multipolygon.reference'));
61867 function makeAddRoleFix(role) {
61868 return new validationIssueFix({
61869 title: _t.html('issues.fix.set_as_' + role + '.title'),
61870 onClick: function onClick(context) {
61871 var oldMember = this.issue.data.member;
61873 id: this.issue.entityIds[1],
61874 type: oldMember.type,
61877 context.perform(actionChangeMember(this.issue.entityIds[0], member, oldMember.index), _t('operations.change_role.annotation', {
61884 validation.type = type;
61888 function validationMissingTag(context) {
61889 var type = 'missing_tag';
61891 function hasDescriptiveTags(entity, graph) {
61892 var keys = Object.keys(entity.tags).filter(function (k) {
61893 if (k === 'area' || k === 'name') {
61896 return osmIsInterestingTag(k);
61900 if (entity.type === 'relation' && keys.length === 1 && entity.tags.type === 'multipolygon') {
61901 // this relation's only interesting tag just says its a multipolygon,
61902 // which is not descriptive enough
61903 // It's okay for a simple multipolygon to have no descriptive tags
61904 // if its outer way has them (old model, see `outdated_tags.js`)
61905 return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
61908 return keys.length > 0;
61911 function isUnknownRoad(entity) {
61912 return entity.type === 'way' && entity.tags.highway === 'road';
61915 function isUntypedRelation(entity) {
61916 return entity.type === 'relation' && !entity.tags.type;
61919 var validation = function checkMissingTag(entity, graph) {
61921 var osm = context.connection();
61922 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
61924 if (!isUnloadedNode && // allow untagged nodes that are part of ways
61925 entity.geometry(graph) !== 'vertex' && // allow untagged entities that are part of relations
61926 !entity.hasParentRelations(graph)) {
61927 if (Object.keys(entity.tags).length === 0) {
61929 } else if (!hasDescriptiveTags(entity, graph)) {
61930 subtype = 'descriptive';
61931 } else if (isUntypedRelation(entity)) {
61932 subtype = 'relation_type';
61934 } // flag an unknown road even if it's a member of a relation
61937 if (!subtype && isUnknownRoad(entity)) {
61938 subtype = 'highway_classification';
61941 if (!subtype) return [];
61942 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
61943 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag'; // can always delete if the user created it in the first place..
61945 var canDelete = entity.version === undefined || entity.v !== undefined;
61946 var severity = canDelete && subtype !== 'highway_classification' ? 'error' : 'warning';
61947 return [new validationIssue({
61950 severity: severity,
61951 message: function message(context) {
61952 var entity = context.hasEntity(this.entityIds[0]);
61953 return entity ? _t.html('issues.' + messageID + '.message', {
61954 feature: utilDisplayLabel(entity, context.graph())
61957 reference: showReference,
61958 entityIds: [entity.id],
61959 dynamicFixes: function dynamicFixes(context) {
61961 var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
61962 fixes.push(new validationIssueFix({
61963 icon: 'iD-icon-search',
61964 title: _t.html('issues.fix.' + selectFixType + '.title'),
61965 onClick: function onClick(context) {
61966 context.ui().sidebar.showPresetList();
61970 var id = this.entityIds[0];
61971 var operation = operationDelete(context, [id]);
61972 var disabledReasonID = operation.disabled();
61974 if (!disabledReasonID) {
61975 deleteOnClick = function deleteOnClick(context) {
61976 var id = this.issue.entityIds[0];
61977 var operation = operationDelete(context, [id]);
61979 if (!operation.disabled()) {
61985 fixes.push(new validationIssueFix({
61986 icon: 'iD-operation-delete',
61987 title: _t.html('issues.fix.delete_feature.title'),
61988 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
61989 onClick: deleteOnClick
61995 function showReference(selection) {
61996 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.' + referenceID + '.reference'));
62000 validation.type = type;
62004 var simplify = function simplify(str) {
62005 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());
62009 // kvnd: "amenity/fast_food|Thaï Express~(North America)",
62010 // kvn: "amenity/fast_food|Thaï Express",
62011 // kv: "amenity/fast_food",
62014 // n: "Thaï Express",
62015 // d: "(North America)",
62016 // nsimple: "thaiexpress",
62017 // kvnnsimple: "amenity/fast_food|thaiexpress"
62020 var to_parts = function to_parts(kvnd) {
62023 var kvndparts = kvnd.split('~', 2);
62024 if (kvndparts.length > 1) parts.d = kvndparts[1];
62025 parts.kvn = kvndparts[0];
62026 var kvnparts = parts.kvn.split('|', 2);
62027 if (kvnparts.length > 1) parts.n = kvnparts[1];
62028 parts.kv = kvnparts[0];
62029 var kvparts = parts.kv.split('/', 2);
62030 parts.k = kvparts[0];
62031 parts.v = kvparts[1];
62032 parts.nsimple = simplify(parts.n);
62033 parts.kvnsimple = parts.kv + '|' + parts.nsimple;
62037 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"]};
62039 matchGroups: matchGroups
62042 var matchGroups$1 = require$$0.matchGroups;
62044 var matcher$1 = function matcher() {
62045 var _warnings = []; // array of match conflict pairs
62047 var _ambiguous = {};
62048 var _matchIndex = {};
62049 var matcher = {}; // Create an index of all the keys/simplenames for fast matching
62051 matcher.buildMatchIndex = function (brands) {
62052 // two passes - once for primary names, once for secondary/alternate names
62053 Object.keys(brands).forEach(function (kvnd) {
62054 return insertNames(kvnd, 'primary');
62056 Object.keys(brands).forEach(function (kvnd) {
62057 return insertNames(kvnd, 'secondary');
62060 function insertNames(kvnd, which) {
62061 var obj = brands[kvnd];
62062 var parts = to_parts(kvnd); // Exit early for ambiguous names in the second pass.
62063 // They were collected in the first pass and we don't gather alt names for them.
62065 if (which === 'secondary' && parts.d) return;
62067 if (obj.countryCodes) {
62068 parts.countryCodes = obj.countryCodes.slice(); // copy
62071 var nomatches = obj.nomatch || [];
62073 if (nomatches.some(function (s) {
62076 console.log("WARNING match/nomatch conflict for ".concat(kvnd));
62080 var match_kv = [parts.kv].concat(obj.matchTags || []).concat(["".concat(parts.k, "/yes"), "building/yes"]) // #3454 - match some generic tags
62081 .map(function (s) {
62082 return s.toLowerCase();
62084 var match_nsimple = [];
62086 if (which === 'primary') {
62087 match_nsimple = [parts.n].concat(obj.matchNames || []).concat(obj.tags.official_name || []) // #2732 - match alternate names
62089 } else if (which === 'secondary') {
62090 match_nsimple = [].concat(obj.tags.alt_name || []) // #2732 - match alternate names
62091 .concat(obj.tags.short_name || []) // #2732 - match alternate names
62095 if (!match_nsimple.length) return; // nothing to do
62097 match_kv.forEach(function (kv) {
62098 match_nsimple.forEach(function (nsimple) {
62100 // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
62101 // FIXME: Name collisions will overwrite the initial entry (ok for now)
62102 if (!_ambiguous[kv]) _ambiguous[kv] = {};
62103 _ambiguous[kv][nsimple] = parts;
62105 // Names we mostly expect to be unique..
62106 if (!_matchIndex[kv]) _matchIndex[kv] = {};
62107 var m = _matchIndex[kv][nsimple];
62110 // There already is a match for this name, skip it
62111 // Warn if we detect collisions in a primary name.
62112 // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
62113 if (which === 'primary' && !/\/yes$/.test(kv)) {
62114 _warnings.push([m.kvnd, "".concat(kvnd, " (").concat(kv, "/").concat(nsimple, ")")]);
62117 _matchIndex[kv][nsimple] = parts; // insert
62123 }; // pass a `key`, `value`, `name` and return the best match,
62124 // `countryCode` optional (if supplied, must match that too)
62127 matcher.matchKVN = function (key, value, name, countryCode) {
62128 return matcher.matchParts(to_parts("".concat(key, "/").concat(value, "|").concat(name)), countryCode);
62129 }; // pass a parts object and return the best match,
62130 // `countryCode` optional (if supplied, must match that too)
62133 matcher.matchParts = function (parts, countryCode) {
62135 var inGroup = false; // fixme: we currently return a single match for ambiguous
62137 match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
62138 if (match && matchesCountryCode(match)) return match; // try to return an exact match
62140 match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
62141 if (match && matchesCountryCode(match)) return match; // look in match groups
62143 for (var mg in matchGroups$1) {
62144 var matchGroup = matchGroups$1[mg];
62148 for (var i = 0; i < matchGroup.length; i++) {
62149 var otherkv = matchGroup[i].toLowerCase();
62152 inGroup = otherkv === parts.kv;
62156 // fixme: we currently return a single match for ambiguous
62157 match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
62161 match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
62164 if (match && !matchesCountryCode(match)) {
62168 if (inGroup && match) {
62176 function matchesCountryCode(match) {
62177 if (!countryCode) return true;
62178 if (!match.countryCodes) return true;
62179 return match.countryCodes.indexOf(countryCode) !== -1;
62183 matcher.getWarnings = function () {
62190 var fromCharCode = String.fromCharCode;
62191 var nativeFromCodePoint = String.fromCodePoint;
62193 // length should be 1, old FF problem
62194 var INCORRECT_LENGTH = !!nativeFromCodePoint && nativeFromCodePoint.length != 1;
62196 // `String.fromCodePoint` method
62197 // https://tc39.github.io/ecma262/#sec-string.fromcodepoint
62198 _export({ target: 'String', stat: true, forced: INCORRECT_LENGTH }, {
62199 fromCodePoint: function fromCodePoint(x) { // eslint-disable-line no-unused-vars
62201 var length = arguments.length;
62204 while (length > i) {
62205 code = +arguments[i++];
62206 if (toAbsoluteIndex(code, 0x10FFFF) !== code) throw RangeError(code + ' is not a valid code point');
62207 elements.push(code < 0x10000
62208 ? fromCharCode(code)
62209 : fromCharCode(((code -= 0x10000) >> 10) + 0xD800, code % 0x400 + 0xDC00)
62211 } return elements.join('');
62215 var quickselect$2 = createCommonjsModule(function (module, exports) {
62216 (function (global, factory) {
62217 module.exports = factory() ;
62218 })(commonjsGlobal, function () {
62220 function quickselect(arr, k, left, right, compare) {
62221 quickselectStep(arr, k, left || 0, right || arr.length - 1, compare || defaultCompare);
62224 function quickselectStep(arr, k, left, right, compare) {
62225 while (right > left) {
62226 if (right - left > 600) {
62227 var n = right - left + 1;
62228 var m = k - left + 1;
62229 var z = Math.log(n);
62230 var s = 0.5 * Math.exp(2 * z / 3);
62231 var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
62232 var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
62233 var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
62234 quickselectStep(arr, k, newLeft, newRight, compare);
62240 swap(arr, left, k);
62241 if (compare(arr[right], t) > 0) swap(arr, left, right);
62248 while (compare(arr[i], t) < 0) {
62252 while (compare(arr[j], t) > 0) {
62257 if (compare(arr[left], t) === 0) swap(arr, left, j);else {
62259 swap(arr, j, right);
62261 if (j <= k) left = j + 1;
62262 if (k <= j) right = j - 1;
62266 function swap(arr, i, j) {
62272 function defaultCompare(a, b) {
62273 return a < b ? -1 : a > b ? 1 : 0;
62276 return quickselect;
62280 var rbush_1 = rbush;
62281 var _default$2 = rbush;
62283 function rbush(maxEntries, format) {
62284 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
62286 this._maxEntries = Math.max(4, maxEntries || 9);
62287 this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
62290 this._initFormat(format);
62296 rbush.prototype = {
62297 all: function all() {
62298 return this._all(this.data, []);
62300 search: function search(bbox) {
62301 var node = this.data,
62303 toBBox = this.toBBox;
62304 if (!intersects$1(bbox, node)) return result;
62305 var nodesToSearch = [],
62312 for (i = 0, len = node.children.length; i < len; i++) {
62313 child = node.children[i];
62314 childBBox = node.leaf ? toBBox(child) : child;
62316 if (intersects$1(bbox, childBBox)) {
62317 if (node.leaf) result.push(child);else if (contains$1(bbox, childBBox)) this._all(child, result);else nodesToSearch.push(child);
62321 node = nodesToSearch.pop();
62326 collides: function collides(bbox) {
62327 var node = this.data,
62328 toBBox = this.toBBox;
62329 if (!intersects$1(bbox, node)) return false;
62330 var nodesToSearch = [],
62337 for (i = 0, len = node.children.length; i < len; i++) {
62338 child = node.children[i];
62339 childBBox = node.leaf ? toBBox(child) : child;
62341 if (intersects$1(bbox, childBBox)) {
62342 if (node.leaf || contains$1(bbox, childBBox)) return true;
62343 nodesToSearch.push(child);
62347 node = nodesToSearch.pop();
62352 load: function load(data) {
62353 if (!(data && data.length)) return this;
62355 if (data.length < this._minEntries) {
62356 for (var i = 0, len = data.length; i < len; i++) {
62357 this.insert(data[i]);
62361 } // recursively build the tree with the given data from scratch using OMT algorithm
62364 var node = this._build(data.slice(), 0, data.length - 1, 0);
62366 if (!this.data.children.length) {
62367 // save as is if tree is empty
62369 } else if (this.data.height === node.height) {
62370 // split root if trees have the same height
62371 this._splitRoot(this.data, node);
62373 if (this.data.height < node.height) {
62374 // swap trees if inserted one is bigger
62375 var tmpNode = this.data;
62378 } // insert the small tree into the large tree at appropriate level
62381 this._insert(node, this.data.height - node.height - 1, true);
62386 insert: function insert(item) {
62387 if (item) this._insert(item, this.data.height - 1);
62390 clear: function clear() {
62391 this.data = createNode$1([]);
62394 remove: function remove(item, equalsFn) {
62395 if (!item) return this;
62396 var node = this.data,
62397 bbox = this.toBBox(item),
62403 goingUp; // depth-first iterative tree traversal
62405 while (node || path.length) {
62409 parent = path[path.length - 1];
62415 // check current node
62416 index = findItem$1(item, node.children, equalsFn);
62418 if (index !== -1) {
62419 // item found, remove the item and condense tree upwards
62420 node.children.splice(index, 1);
62423 this._condense(path);
62429 if (!goingUp && !node.leaf && contains$1(node, bbox)) {
62435 node = node.children[0];
62436 } else if (parent) {
62439 node = parent.children[i];
62441 } else node = null; // nothing found
62447 toBBox: function toBBox(item) {
62450 compareMinX: compareNodeMinX$1,
62451 compareMinY: compareNodeMinY$1,
62452 toJSON: function toJSON() {
62455 fromJSON: function fromJSON(data) {
62459 _all: function _all(node, result) {
62460 var nodesToSearch = [];
62463 if (node.leaf) result.push.apply(result, node.children);else nodesToSearch.push.apply(nodesToSearch, node.children);
62464 node = nodesToSearch.pop();
62469 _build: function _build(items, left, right, height) {
62470 var N = right - left + 1,
62471 M = this._maxEntries,
62475 // reached leaf level; return leaf
62476 node = createNode$1(items.slice(left, right + 1));
62477 calcBBox$1(node, this.toBBox);
62482 // target height of the bulk-loaded tree
62483 height = Math.ceil(Math.log(N) / Math.log(M)); // target number of root entries to maximize storage utilization
62485 M = Math.ceil(N / Math.pow(M, height - 1));
62488 node = createNode$1([]);
62490 node.height = height; // split the items into M mostly square tiles
62492 var N2 = Math.ceil(N / M),
62493 N1 = N2 * Math.ceil(Math.sqrt(M)),
62498 multiSelect$1(items, left, right, N1, this.compareMinX);
62500 for (i = left; i <= right; i += N1) {
62501 right2 = Math.min(i + N1 - 1, right);
62502 multiSelect$1(items, i, right2, N2, this.compareMinY);
62504 for (j = i; j <= right2; j += N2) {
62505 right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively
62507 node.children.push(this._build(items, j, right3, height - 1));
62511 calcBBox$1(node, this.toBBox);
62514 _chooseSubtree: function _chooseSubtree(bbox, node, level, path) {
62515 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
62519 if (node.leaf || path.length - 1 === level) break;
62520 minArea = minEnlargement = Infinity;
62522 for (i = 0, len = node.children.length; i < len; i++) {
62523 child = node.children[i];
62524 area = bboxArea$1(child);
62525 enlargement = enlargedArea$1(bbox, child) - area; // choose entry with the least area enlargement
62527 if (enlargement < minEnlargement) {
62528 minEnlargement = enlargement;
62529 minArea = area < minArea ? area : minArea;
62530 targetNode = child;
62531 } else if (enlargement === minEnlargement) {
62532 // otherwise choose one with the smallest area
62533 if (area < minArea) {
62535 targetNode = child;
62540 node = targetNode || node.children[0];
62545 _insert: function _insert(item, level, isNode) {
62546 var toBBox = this.toBBox,
62547 bbox = isNode ? item : toBBox(item),
62548 insertPath = []; // find the best node for accommodating the item, saving all nodes along the path too
62550 var node = this._chooseSubtree(bbox, this.data, level, insertPath); // put the item into the node
62553 node.children.push(item);
62554 extend$3(node, bbox); // split on node overflow; propagate upwards if necessary
62556 while (level >= 0) {
62557 if (insertPath[level].children.length > this._maxEntries) {
62558 this._split(insertPath, level);
62562 } // adjust bboxes along the insertion path
62565 this._adjustParentBBoxes(bbox, insertPath, level);
62567 // split overflowed node into two
62568 _split: function _split(insertPath, level) {
62569 var node = insertPath[level],
62570 M = node.children.length,
62571 m = this._minEntries;
62573 this._chooseSplitAxis(node, m, M);
62575 var splitIndex = this._chooseSplitIndex(node, m, M);
62577 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
62578 newNode.height = node.height;
62579 newNode.leaf = node.leaf;
62580 calcBBox$1(node, this.toBBox);
62581 calcBBox$1(newNode, this.toBBox);
62582 if (level) insertPath[level - 1].children.push(newNode);else this._splitRoot(node, newNode);
62584 _splitRoot: function _splitRoot(node, newNode) {
62586 this.data = createNode$1([node, newNode]);
62587 this.data.height = node.height + 1;
62588 this.data.leaf = false;
62589 calcBBox$1(this.data, this.toBBox);
62591 _chooseSplitIndex: function _chooseSplitIndex(node, m, M) {
62592 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
62593 minOverlap = minArea = Infinity;
62595 for (i = m; i <= M - m; i++) {
62596 bbox1 = distBBox$1(node, 0, i, this.toBBox);
62597 bbox2 = distBBox$1(node, i, M, this.toBBox);
62598 overlap = intersectionArea$1(bbox1, bbox2);
62599 area = bboxArea$1(bbox1) + bboxArea$1(bbox2); // choose distribution with minimum overlap
62601 if (overlap < minOverlap) {
62602 minOverlap = overlap;
62604 minArea = area < minArea ? area : minArea;
62605 } else if (overlap === minOverlap) {
62606 // otherwise choose distribution with minimum area
62607 if (area < minArea) {
62616 // sorts node children by the best axis for split
62617 _chooseSplitAxis: function _chooseSplitAxis(node, m, M) {
62618 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
62619 compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
62620 xMargin = this._allDistMargin(node, m, M, compareMinX),
62621 yMargin = this._allDistMargin(node, m, M, compareMinY); // if total distributions margin value is minimal for x, sort by minX,
62622 // otherwise it's already sorted by minY
62625 if (xMargin < yMargin) node.children.sort(compareMinX);
62627 // total margin of all possible split distributions where each node is at least m full
62628 _allDistMargin: function _allDistMargin(node, m, M, compare) {
62629 node.children.sort(compare);
62630 var toBBox = this.toBBox,
62631 leftBBox = distBBox$1(node, 0, m, toBBox),
62632 rightBBox = distBBox$1(node, M - m, M, toBBox),
62633 margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
62637 for (i = m; i < M - m; i++) {
62638 child = node.children[i];
62639 extend$3(leftBBox, node.leaf ? toBBox(child) : child);
62640 margin += bboxMargin$1(leftBBox);
62643 for (i = M - m - 1; i >= m; i--) {
62644 child = node.children[i];
62645 extend$3(rightBBox, node.leaf ? toBBox(child) : child);
62646 margin += bboxMargin$1(rightBBox);
62651 _adjustParentBBoxes: function _adjustParentBBoxes(bbox, path, level) {
62652 // adjust bboxes along the given tree path
62653 for (var i = level; i >= 0; i--) {
62654 extend$3(path[i], bbox);
62657 _condense: function _condense(path) {
62658 // go through the path, removing empty nodes and updating bboxes
62659 for (var i = path.length - 1, siblings; i >= 0; i--) {
62660 if (path[i].children.length === 0) {
62662 siblings = path[i - 1].children;
62663 siblings.splice(siblings.indexOf(path[i]), 1);
62664 } else this.clear();
62665 } else calcBBox$1(path[i], this.toBBox);
62668 _initFormat: function _initFormat(format) {
62669 // data format (minX, minY, maxX, maxY accessors)
62670 // uses eval-type function compilation instead of just accepting a toBBox function
62671 // because the algorithms are very sensitive to sorting functions performance,
62672 // so they should be dead simple and without inner calls
62673 var compareArr = ['return a', ' - b', ';'];
62674 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
62675 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
62676 this.toBBox = new Function('a', 'return {minX: a' + format[0] + ', minY: a' + format[1] + ', maxX: a' + format[2] + ', maxY: a' + format[3] + '};');
62680 function findItem$1(item, items, equalsFn) {
62681 if (!equalsFn) return items.indexOf(item);
62683 for (var i = 0; i < items.length; i++) {
62684 if (equalsFn(item, items[i])) return i;
62688 } // calculate node's bbox from bboxes of its children
62691 function calcBBox$1(node, toBBox) {
62692 distBBox$1(node, 0, node.children.length, toBBox, node);
62693 } // min bounding rectangle of node children from k to p-1
62696 function distBBox$1(node, k, p, toBBox, destNode) {
62697 if (!destNode) destNode = createNode$1(null);
62698 destNode.minX = Infinity;
62699 destNode.minY = Infinity;
62700 destNode.maxX = -Infinity;
62701 destNode.maxY = -Infinity;
62703 for (var i = k, child; i < p; i++) {
62704 child = node.children[i];
62705 extend$3(destNode, node.leaf ? toBBox(child) : child);
62711 function extend$3(a, b) {
62712 a.minX = Math.min(a.minX, b.minX);
62713 a.minY = Math.min(a.minY, b.minY);
62714 a.maxX = Math.max(a.maxX, b.maxX);
62715 a.maxY = Math.max(a.maxY, b.maxY);
62719 function compareNodeMinX$1(a, b) {
62720 return a.minX - b.minX;
62723 function compareNodeMinY$1(a, b) {
62724 return a.minY - b.minY;
62727 function bboxArea$1(a) {
62728 return (a.maxX - a.minX) * (a.maxY - a.minY);
62731 function bboxMargin$1(a) {
62732 return a.maxX - a.minX + (a.maxY - a.minY);
62735 function enlargedArea$1(a, b) {
62736 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));
62739 function intersectionArea$1(a, b) {
62740 var minX = Math.max(a.minX, b.minX),
62741 minY = Math.max(a.minY, b.minY),
62742 maxX = Math.min(a.maxX, b.maxX),
62743 maxY = Math.min(a.maxY, b.maxY);
62744 return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
62747 function contains$1(a, b) {
62748 return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
62751 function intersects$1(a, b) {
62752 return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
62755 function createNode$1(children) {
62757 children: children,
62765 } // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
62766 // combines selection algorithm with binary divide & conquer approach
62769 function multiSelect$1(arr, left, right, n, compare) {
62770 var stack = [left, right],
62773 while (stack.length) {
62774 right = stack.pop();
62775 left = stack.pop();
62776 if (right - left <= n) continue;
62777 mid = left + Math.ceil((right - left) / n / 2) * n;
62778 quickselect$2(arr, mid, left, right, compare);
62779 stack.push(left, mid, mid, right);
62782 rbush_1["default"] = _default$2;
62784 var lineclip_1$1 = lineclip$1;
62785 lineclip$1.polyline = lineclip$1;
62786 lineclip$1.polygon = polygonclip$1; // Cohen-Sutherland line clippign algorithm, adapted to efficiently
62787 // handle polylines rather than just segments
62789 function lineclip$1(points, bbox, result) {
62790 var len = points.length,
62791 codeA = bitCode$1(points[0], bbox),
62798 if (!result) result = [];
62800 for (i = 1; i < len; i++) {
62803 codeB = lastCode = bitCode$1(b, bbox);
62806 if (!(codeA | codeB)) {
62810 if (codeB !== lastCode) {
62811 // segment went outside
62815 // start a new line
62819 } else if (i === len - 1) {
62824 } else if (codeA & codeB) {
62827 } else if (codeA) {
62828 // a outside, intersect with clip edge
62829 a = intersect$1(a, b, codeA, bbox);
62830 codeA = bitCode$1(a, bbox);
62833 b = intersect$1(a, b, codeB, bbox);
62834 codeB = bitCode$1(b, bbox);
62841 if (part.length) result.push(part);
62843 } // Sutherland-Hodgeman polygon clipping algorithm
62846 function polygonclip$1(points, bbox) {
62847 var result, edge, prev, prevInside, i, p, inside; // clip against each side of the clip rectangle
62849 for (edge = 1; edge <= 8; edge *= 2) {
62851 prev = points[points.length - 1];
62852 prevInside = !(bitCode$1(prev, bbox) & edge);
62854 for (i = 0; i < points.length; i++) {
62856 inside = !(bitCode$1(p, bbox) & edge); // if segment goes through the clip window, add an intersection
62858 if (inside !== prevInside) result.push(intersect$1(prev, p, edge, bbox));
62859 if (inside) result.push(p); // add a point if it's inside
62862 prevInside = inside;
62866 if (!points.length) break;
62870 } // intersect a segment against one of the 4 lines that make up the bbox
62873 function intersect$1(a, b, edge, bbox) {
62874 return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
62875 edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
62876 edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
62877 edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
62879 } // bit code reflects the point position relative to the bbox:
62881 // top 1001 1000 1010
62882 // mid 0001 0000 0010
62883 // bottom 0101 0100 0110
62886 function bitCode$1(p, bbox) {
62888 if (p[0] < bbox[0]) code |= 1; // left
62889 else if (p[0] > bbox[2]) code |= 2; // right
62891 if (p[1] < bbox[1]) code |= 4; // bottom
62892 else if (p[1] > bbox[3]) code |= 8; // top
62897 var whichPolygon_1 = whichPolygon;
62899 function whichPolygon(data) {
62902 for (var i = 0; i < data.features.length; i++) {
62903 var feature = data.features[i];
62904 var coords = feature.geometry.coordinates;
62906 if (feature.geometry.type === 'Polygon') {
62907 bboxes.push(treeItem(coords, feature.properties));
62908 } else if (feature.geometry.type === 'MultiPolygon') {
62909 for (var j = 0; j < coords.length; j++) {
62910 bboxes.push(treeItem(coords[j], feature.properties));
62915 var tree = rbush_1().load(bboxes);
62917 function query(p, multi) {
62919 result = tree.search({
62926 for (var i = 0; i < result.length; i++) {
62927 if (insidePolygon(result[i].coords, p)) {
62928 if (multi) output.push(result[i].props);else return result[i].props;
62932 return multi && output.length ? output : null;
62937 query.bbox = function queryBBox(bbox) {
62939 var result = tree.search({
62946 for (var i = 0; i < result.length; i++) {
62947 if (polygonIntersectsBBox(result[i].coords, bbox)) {
62948 output.push(result[i].props);
62958 function polygonIntersectsBBox(polygon, bbox) {
62959 var bboxCenter = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
62960 if (insidePolygon(polygon, bboxCenter)) return true;
62962 for (var i = 0; i < polygon.length; i++) {
62963 if (lineclip_1$1(polygon[i], bbox).length > 0) return true;
62967 } // ray casting algorithm for detecting if point is in polygon
62970 function insidePolygon(rings, p) {
62971 var inside = false;
62973 for (var i = 0, len = rings.length; i < len; i++) {
62974 var ring = rings[i];
62976 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
62977 if (rayIntersect(p, ring[j], ring[k])) inside = !inside;
62984 function rayIntersect(p, p1, p2) {
62985 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];
62988 function treeItem(coords, props) {
62998 for (var i = 0; i < coords[0].length; i++) {
62999 var p = coords[0][i];
63000 item.minX = Math.min(item.minX, p[0]);
63001 item.minY = Math.min(item.minY, p[1]);
63002 item.maxX = Math.max(item.maxX, p[0]);
63003 item.maxY = Math.max(item.maxY, p[1]);
63009 var type = "FeatureCollection";
63010 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]]]]}}];
63016 var borders = rawBorders;
63017 var whichPolygonGetter = {};
63018 var featuresByCode = {};
63019 var idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
63020 var levels = ['subterritory', 'territory', 'country', 'intermediateRegion', 'subregion', 'region', 'union', 'world'];
63021 loadDerivedDataAndCaches(borders);
63023 function loadDerivedDataAndCaches(borders) {
63024 var identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
63025 var geometryFeatures = [];
63027 for (var i in borders.features) {
63028 var _feature = borders.features[i];
63029 _feature.properties.id = _feature.properties.iso1A2 || _feature.properties.m49;
63031 loadIsoStatus(_feature);
63032 loadLevel(_feature);
63033 loadGroups(_feature);
63034 loadRoadSpeedUnit(_feature);
63035 loadDriveSide(_feature);
63036 loadFlag(_feature);
63037 cacheFeatureByIDs(_feature);
63038 if (_feature.geometry) geometryFeatures.push(_feature);
63041 for (var _i in borders.features) {
63042 var _feature2 = borders.features[_i];
63044 _feature2.properties.groups.sort(function (groupID1, groupID2) {
63045 return levels.indexOf(featuresByCode[groupID1].properties.level) - levels.indexOf(featuresByCode[groupID2].properties.level);
63048 loadMembersForGroupsOf(_feature2);
63051 var geometryOnlyCollection = {
63052 type: 'RegionFeatureCollection',
63053 features: geometryFeatures
63055 whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
63057 function loadGroups(feature) {
63058 var props = feature.properties;
63060 if (!props.groups) {
63064 if (props.country) {
63065 props.groups.push(props.country);
63068 if (props.m49 !== '001') {
63069 props.groups.push('001');
63073 function loadM49(feature) {
63074 var props = feature.properties;
63076 if (!props.m49 && props.iso1N3) {
63077 props.m49 = props.iso1N3;
63081 function loadIsoStatus(feature) {
63082 var props = feature.properties;
63084 if (!props.isoStatus && props.iso1A2) {
63085 props.isoStatus = 'official';
63089 function loadLevel(feature) {
63090 var props = feature.properties;
63091 if (props.level) return;
63093 if (!props.country) {
63094 props.level = 'country';
63095 } else if (props.isoStatus === 'official') {
63096 props.level = 'territory';
63098 props.level = 'subterritory';
63102 function loadRoadSpeedUnit(feature) {
63103 var props = feature.properties;
63105 if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
63106 props.roadSpeedUnit = 'km/h';
63110 function loadDriveSide(feature) {
63111 var props = feature.properties;
63113 if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
63114 props.driveSide = 'right';
63118 function loadFlag(feature) {
63119 if (!feature.properties.iso1A2) return;
63120 var flag = feature.properties.iso1A2.replace(/./g, function (_char) {
63121 return String.fromCodePoint(_char.charCodeAt(0) + 127397);
63123 feature.properties.emojiFlag = flag;
63126 function loadMembersForGroupsOf(feature) {
63127 var featureID = feature.properties.id;
63128 var standardizedGroupIDs = [];
63130 for (var j in feature.properties.groups) {
63131 var groupID = feature.properties.groups[j];
63132 var groupFeature = featuresByCode[groupID];
63133 standardizedGroupIDs.push(groupFeature.properties.id);
63135 if (groupFeature.properties.members) {
63136 groupFeature.properties.members.push(featureID);
63138 groupFeature.properties.members = [featureID];
63142 feature.properties.groups = standardizedGroupIDs;
63145 function cacheFeatureByIDs(feature) {
63146 for (var k in identifierProps) {
63147 var prop = identifierProps[k];
63148 var id = prop && feature.properties[prop];
63151 id = id.replace(idFilterRegex, '').toUpperCase();
63152 featuresByCode[id] = feature;
63156 if (feature.properties.aliases) {
63157 for (var j in feature.properties.aliases) {
63158 var alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
63159 featuresByCode[alias] = feature;
63165 function locArray(loc) {
63166 if (Array.isArray(loc)) {
63168 } else if (loc.coordinates) {
63169 return loc.coordinates;
63172 return loc.geometry.coordinates;
63175 function smallestFeature(loc) {
63176 var query = locArray(loc);
63177 var featureProperties = whichPolygonGetter(query);
63178 if (!featureProperties) return null;
63179 return featuresByCode[featureProperties.id];
63182 function countryFeature(loc) {
63183 var feature = smallestFeature(loc);
63184 if (!feature) return null;
63185 var countryCode = feature.properties.country || feature.properties.iso1A2;
63186 return featuresByCode[countryCode];
63189 function featureForLoc(loc, opts) {
63190 if (opts && opts.level && opts.level !== 'country') {
63191 var features = featuresContaining(loc);
63192 var targetLevel = opts.level;
63193 var targetLevelIndex = levels.indexOf(targetLevel);
63194 if (targetLevelIndex === -1) return null;
63196 for (var i in features) {
63197 var _feature3 = features[i];
63199 if (_feature3.properties.level === targetLevel || levels.indexOf(_feature3.properties.level) > targetLevelIndex) {
63207 return countryFeature(loc);
63210 function featureForID(id) {
63213 if (typeof id === 'number') {
63214 stringID = id.toString();
63216 if (stringID.length === 1) {
63217 stringID = '00' + stringID;
63218 } else if (stringID.length === 2) {
63219 stringID = '0' + stringID;
63222 stringID = id.replace(idFilterRegex, '').toUpperCase();
63225 return featuresByCode[stringID] || null;
63228 function smallestOrMatchingFeature(query) {
63229 if (_typeof(query) === 'object') {
63230 return smallestFeature(query);
63233 return featureForID(query);
63236 function feature(query, opts) {
63237 if (_typeof(query) === 'object') {
63238 return featureForLoc(query, opts);
63241 return featureForID(query);
63243 function iso1A2Code(query, opts) {
63244 var match = feature(query, opts);
63245 if (!match) return null;
63246 return match.properties.iso1A2 || null;
63248 function featuresContaining(query, strict) {
63249 var feature = smallestOrMatchingFeature(query);
63250 if (!feature) return [];
63253 if (!strict || _typeof(query) === 'object') {
63254 features.push(feature);
63257 var properties = feature.properties;
63259 for (var i in properties.groups) {
63260 var groupID = properties.groups[i];
63261 features.push(featuresByCode[groupID]);
63266 function roadSpeedUnit(query) {
63267 var feature = smallestOrMatchingFeature(query);
63268 return feature && feature.properties.roadSpeedUnit || null;
63271 var _dataDeprecated;
63275 function validationOutdatedTags() {
63276 var type = 'outdated_tags';
63277 var nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office']; // A concern here in switching to async data means that `_dataDeprecated`
63278 // and `_nsi` will not be available at first, so the data on early tiles
63279 // may not have tags validated fully.
63280 // initialize deprecated tags array
63282 _mainFileFetcher.get('deprecated').then(function (d) {
63283 return _dataDeprecated = d;
63284 })["catch"](function () {
63287 _mainFileFetcher.get('nsi_brands').then(function (d) {
63290 matcher: matcher$1(),
63293 }; // initialize name-suggestion-index matcher
63295 _nsi.matcher.buildMatchIndex(d.brands); // index all known wikipedia and wikidata tags
63298 Object.keys(d.brands).forEach(function (kvnd) {
63299 var brand = d.brands[kvnd];
63300 var wd = brand.tags['brand:wikidata'];
63301 var wp = brand.tags['brand:wikipedia'];
63304 _nsi.wikidata[wd] = kvnd;
63308 _nsi.wikipedia[wp] = kvnd;
63312 })["catch"](function () {
63316 function oldTagIssues(entity, graph) {
63317 var oldTags = Object.assign({}, entity.tags); // shallow copy
63319 var preset = _mainPresetIndex.match(entity, graph);
63320 var subtype = 'deprecated_tags';
63321 if (!preset) return []; // upgrade preset..
63323 if (preset.replacement) {
63324 var newPreset = _mainPresetIndex.item(preset.replacement);
63325 graph = actionChangePreset(entity.id, preset, newPreset, true
63326 /* skip field defaults */
63328 entity = graph.entity(entity.id);
63329 preset = newPreset;
63330 } // upgrade tags..
63333 if (_dataDeprecated) {
63334 var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
63336 if (deprecatedTags.length) {
63337 deprecatedTags.forEach(function (tag) {
63338 graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
63340 entity = graph.entity(entity.id);
63342 } // add missing addTags..
63345 var newTags = Object.assign({}, entity.tags); // shallow copy
63347 if (preset.tags !== preset.addTags) {
63348 Object.keys(preset.addTags).forEach(function (k) {
63350 if (preset.addTags[k] === '*') {
63351 newTags[k] = 'yes';
63353 newTags[k] = preset.addTags[k];
63360 // Do `wikidata` or `wikipedia` identify this entity as a brand? #6416
63361 // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
63364 if (newTags.wikidata) {
63365 // try matching `wikidata`
63366 isBrand = _nsi.wikidata[newTags.wikidata];
63369 if (!isBrand && newTags.wikipedia) {
63370 // fallback to `wikipedia`
63371 isBrand = _nsi.wikipedia[newTags.wikipedia];
63374 if (isBrand && !newTags.office) {
63375 // but avoid doing this for corporate offices
63376 if (newTags.wikidata) {
63377 newTags['brand:wikidata'] = newTags.wikidata;
63378 delete newTags.wikidata;
63381 if (newTags.wikipedia) {
63382 newTags['brand:wikipedia'] = newTags.wikipedia;
63383 delete newTags.wikipedia;
63384 } // I considered setting `name` and other tags here, but they aren't unique per wikidata
63385 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
63386 // So users will really need to use a preset or assign `name` themselves.
63388 } // try key/value|name match against name-suggestion-index
63391 if (newTags.name) {
63392 for (var i = 0; i < nsiKeys.length; i++) {
63393 var k = nsiKeys[i];
63394 if (!newTags[k]) continue;
63395 var center = entity.extent(graph).center();
63396 var countryCode = iso1A2Code(center);
63398 var match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
63400 if (!match) continue; // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
63402 if (match.d) continue;
63403 var brand = _nsi.brands[match.kvnd];
63405 if (brand && brand.tags['brand:wikidata'] && brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
63406 subtype = 'noncanonical_brand';
63407 var keepTags = ['takeaway'].reduce(function (acc, k) {
63409 acc[k] = newTags[k];
63414 nsiKeys.forEach(function (k) {
63415 return delete newTags[k];
63417 Object.assign(newTags, brand.tags, keepTags);
63422 } // determine diff
63425 var tagDiff = utilTagDiff(oldTags, newTags);
63426 if (!tagDiff.length) return [];
63427 var isOnlyAddingTags = tagDiff.every(function (d) {
63428 return d.type === '+';
63432 if (subtype === 'noncanonical_brand') {
63433 prefix = 'noncanonical_brand.';
63434 } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
63435 subtype = 'incomplete_tags';
63436 prefix = 'incomplete.';
63437 } // don't allow autofixing brand tags
63440 var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
63441 return [new validationIssue({
63444 severity: 'warning',
63445 message: showMessage,
63446 reference: showReference,
63447 entityIds: [entity.id],
63448 hash: JSON.stringify(tagDiff),
63449 dynamicFixes: function dynamicFixes() {
63450 return [new validationIssueFix({
63451 autoArgs: autoArgs,
63452 title: _t.html('issues.fix.upgrade_tags.title'),
63453 onClick: function onClick(context) {
63454 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
63460 function doUpgrade(graph) {
63461 var currEntity = graph.hasEntity(entity.id);
63462 if (!currEntity) return graph;
63463 var newTags = Object.assign({}, currEntity.tags); // shallow copy
63465 tagDiff.forEach(function (diff) {
63466 if (diff.type === '-') {
63467 delete newTags[diff.key];
63468 } else if (diff.type === '+') {
63469 newTags[diff.key] = diff.newVal;
63472 return actionChangeTags(currEntity.id, newTags)(graph);
63475 function showMessage(context) {
63476 var currEntity = context.hasEntity(entity.id);
63477 if (!currEntity) return '';
63478 var messageID = "issues.outdated_tags.".concat(prefix, "message");
63480 if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
63481 messageID += '_incomplete';
63484 return _t.html(messageID, {
63485 feature: utilDisplayLabel(currEntity, context.graph())
63489 function showReference(selection) {
63490 var enter = selection.selectAll('.issue-reference').data([0]).enter();
63491 enter.append('div').attr('class', 'issue-reference').html(_t.html("issues.outdated_tags.".concat(prefix, "reference")));
63492 enter.append('strong').html(_t.html('issues.suggested'));
63493 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) {
63494 var klass = d.type === '+' ? 'add' : 'remove';
63495 return "tagDiff-cell tagDiff-cell-".concat(klass);
63496 }).html(function (d) {
63502 function oldMultipolygonIssues(entity, graph) {
63503 var multipolygon, outerWay;
63505 if (entity.type === 'relation') {
63506 outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
63507 multipolygon = entity;
63508 } else if (entity.type === 'way') {
63509 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
63515 if (!multipolygon || !outerWay) return [];
63516 return [new validationIssue({
63518 subtype: 'old_multipolygon',
63519 severity: 'warning',
63520 message: showMessage,
63521 reference: showReference,
63522 entityIds: [outerWay.id, multipolygon.id],
63523 dynamicFixes: function dynamicFixes() {
63524 return [new validationIssueFix({
63525 autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
63526 title: _t.html('issues.fix.move_tags.title'),
63527 onClick: function onClick(context) {
63528 context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
63534 function doUpgrade(graph) {
63535 var currMultipolygon = graph.hasEntity(multipolygon.id);
63536 var currOuterWay = graph.hasEntity(outerWay.id);
63537 if (!currMultipolygon || !currOuterWay) return graph;
63538 currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
63539 graph = graph.replace(currMultipolygon);
63540 return actionChangeTags(currOuterWay.id, {})(graph);
63543 function showMessage(context) {
63544 var currMultipolygon = context.hasEntity(multipolygon.id);
63545 if (!currMultipolygon) return '';
63546 return _t.html('issues.old_multipolygon.message', {
63547 multipolygon: utilDisplayLabel(currMultipolygon, context.graph())
63551 function showReference(selection) {
63552 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.old_multipolygon.reference'));
63556 var validation = function checkOutdatedTags(entity, graph) {
63557 var issues = oldMultipolygonIssues(entity, graph);
63558 if (!issues.length) issues = oldTagIssues(entity, graph);
63562 validation.type = type;
63566 function validationPrivateData() {
63567 var type = 'private_data'; // assume that some buildings are private
63569 var privateBuildingValues = {
63575 semidetached_house: true,
63576 static_caravan: true
63577 }; // but they might be public if they have one of these other tags
63587 }; // these tags may contain personally identifying info
63589 var personalTags = {
63590 'contact:email': true,
63591 'contact:fax': true,
63592 'contact:phone': true,
63598 var validation = function checkPrivateData(entity) {
63599 var tags = entity.tags;
63600 if (!tags.building || !privateBuildingValues[tags.building]) return [];
63603 for (var k in tags) {
63604 if (publicKeys[k]) return []; // probably a public feature
63606 if (!personalTags[k]) {
63607 keepTags[k] = tags[k];
63611 var tagDiff = utilTagDiff(tags, keepTags);
63612 if (!tagDiff.length) return [];
63613 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
63614 return [new validationIssue({
63616 severity: 'warning',
63617 message: showMessage,
63618 reference: showReference,
63619 entityIds: [entity.id],
63620 dynamicFixes: function dynamicFixes() {
63621 return [new validationIssueFix({
63622 icon: 'iD-operation-delete',
63623 title: _t.html('issues.fix.' + fixID + '.title'),
63624 onClick: function onClick(context) {
63625 context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
63631 function doUpgrade(graph) {
63632 var currEntity = graph.hasEntity(entity.id);
63633 if (!currEntity) return graph;
63634 var newTags = Object.assign({}, currEntity.tags); // shallow copy
63636 tagDiff.forEach(function (diff) {
63637 if (diff.type === '-') {
63638 delete newTags[diff.key];
63639 } else if (diff.type === '+') {
63640 newTags[diff.key] = diff.newVal;
63643 return actionChangeTags(currEntity.id, newTags)(graph);
63646 function showMessage(context) {
63647 var currEntity = context.hasEntity(this.entityIds[0]);
63648 if (!currEntity) return '';
63649 return _t.html('issues.private_data.contact.message', {
63650 feature: utilDisplayLabel(currEntity, context.graph())
63654 function showReference(selection) {
63655 var enter = selection.selectAll('.issue-reference').data([0]).enter();
63656 enter.append('div').attr('class', 'issue-reference').html(_t.html('issues.private_data.reference'));
63657 enter.append('strong').html(_t.html('issues.suggested'));
63658 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) {
63659 var klass = d.type === '+' ? 'add' : 'remove';
63660 return 'tagDiff-cell tagDiff-cell-' + klass;
63661 }).html(function (d) {
63667 validation.type = type;
63671 var _discardNameRegexes = [];
63672 function validationSuspiciousName() {
63673 var type = 'suspicious_name';
63674 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
63675 // be available at first, so the data on early tiles may not have tags validated fully.
63677 _mainFileFetcher.get('nsi_filters').then(function (filters) {
63678 // known list of generic names (e.g. "bar")
63679 _discardNameRegexes = filters.discardNames.map(function (discardName) {
63680 return new RegExp(discardName, 'i');
63682 })["catch"](function () {
63686 function isDiscardedSuggestionName(lowercaseName) {
63687 return _discardNameRegexes.some(function (regex) {
63688 return regex.test(lowercaseName);
63690 } // test if the name is just the key or tag value (e.g. "park")
63693 function nameMatchesRawTag(lowercaseName, tags) {
63694 for (var i = 0; i < keysToTestForGenericValues.length; i++) {
63695 var key = keysToTestForGenericValues[i];
63696 var val = tags[key];
63699 val = val.toLowerCase();
63701 if (key === lowercaseName || val === lowercaseName || key.replace(/\_/g, ' ') === lowercaseName || val.replace(/\_/g, ' ') === lowercaseName) {
63710 function isGenericName(name, tags) {
63711 name = name.toLowerCase();
63712 return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
63715 function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
63716 return new validationIssue({
63718 subtype: 'generic_name',
63719 severity: 'warning',
63720 message: function message(context) {
63721 var entity = context.hasEntity(this.entityIds[0]);
63722 if (!entity) return '';
63723 var preset = _mainPresetIndex.match(entity, context.graph());
63724 var langName = langCode && _mainLocalizer.languageName(langCode);
63725 return _t.html('issues.generic_name.message' + (langName ? '_language' : ''), {
63726 feature: preset.name(),
63731 reference: showReference,
63732 entityIds: [entityId],
63733 hash: nameKey + '=' + genericName,
63734 dynamicFixes: function dynamicFixes() {
63735 return [new validationIssueFix({
63736 icon: 'iD-operation-delete',
63737 title: _t.html('issues.fix.remove_the_name.title'),
63738 onClick: function onClick(context) {
63739 var entityId = this.issue.entityIds[0];
63740 var entity = context.entity(entityId);
63741 var tags = Object.assign({}, entity.tags); // shallow copy
63743 delete tags[nameKey];
63744 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation'));
63750 function showReference(selection) {
63751 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
63755 function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
63756 return new validationIssue({
63758 subtype: 'not_name',
63759 severity: 'warning',
63760 message: function message(context) {
63761 var entity = context.hasEntity(this.entityIds[0]);
63762 if (!entity) return '';
63763 var preset = _mainPresetIndex.match(entity, context.graph());
63764 var langName = langCode && _mainLocalizer.languageName(langCode);
63765 return _t.html('issues.incorrect_name.message' + (langName ? '_language' : ''), {
63766 feature: preset.name(),
63767 name: incorrectName,
63771 reference: showReference,
63772 entityIds: [entityId],
63773 hash: nameKey + '=' + incorrectName,
63774 dynamicFixes: function dynamicFixes() {
63775 return [new validationIssueFix({
63776 icon: 'iD-operation-delete',
63777 title: _t.html('issues.fix.remove_the_name.title'),
63778 onClick: function onClick(context) {
63779 var entityId = this.issue.entityIds[0];
63780 var entity = context.entity(entityId);
63781 var tags = Object.assign({}, entity.tags); // shallow copy
63783 delete tags[nameKey];
63784 context.perform(actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation'));
63790 function showReference(selection) {
63791 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.generic_name.reference'));
63795 var validation = function checkGenericName(entity) {
63796 // a generic name is okay if it's a known brand or entity
63797 if (entity.hasWikidata()) return [];
63799 var notNames = (entity.tags['not:name'] || '').split(';');
63801 for (var key in entity.tags) {
63802 var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
63804 var langCode = m.length >= 2 ? m[1] : null;
63805 var value = entity.tags[key];
63807 if (notNames.length) {
63808 for (var i in notNames) {
63809 var notName = notNames[i];
63811 if (notName && value === notName) {
63812 issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
63818 if (isGenericName(value, entity.tags)) {
63819 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
63826 validation.type = type;
63830 function validationUnsquareWay(context) {
63831 var type = 'unsquare_way';
63832 var DEFAULT_DEG_THRESHOLD = 5; // see also issues.js
63833 // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
63835 var epsilon = 0.05;
63836 var nodeThreshold = 10;
63838 function isBuilding(entity, graph) {
63839 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') return false;
63840 return entity.tags.building && entity.tags.building !== 'no';
63843 var validation = function checkUnsquareWay(entity, graph) {
63844 if (!isBuilding(entity, graph)) return []; // don't flag ways marked as physically unsquare
63846 if (entity.tags.nonsquare === 'yes') return [];
63847 var isClosed = entity.isClosed();
63848 if (!isClosed) return []; // this building has bigger problems
63849 // don't flag ways with lots of nodes since they are likely detail-mapped
63851 var nodes = graph.childNodes(entity).slice(); // shallow copy
63853 if (nodes.length > nodeThreshold + 1) return []; // +1 because closing node appears twice
63854 // ignore if not all nodes are fully downloaded
63856 var osm = services.osm;
63857 if (!osm || nodes.some(function (node) {
63858 return !osm.isDataLoaded(node.loc);
63859 })) return []; // don't flag connected ways to avoid unresolvable unsquare loops
63861 var hasConnectedSquarableWays = nodes.some(function (node) {
63862 return graph.parentWays(node).some(function (way) {
63863 if (way.id === entity.id) return false;
63864 if (isBuilding(way, graph)) return true;
63865 return graph.parentRelations(way).some(function (parentRelation) {
63866 return parentRelation.isMultipolygon() && parentRelation.tags.building && parentRelation.tags.building !== 'no';
63870 if (hasConnectedSquarableWays) return []; // user-configurable square threshold
63872 var storedDegreeThreshold = corePreferences('validate-square-degrees');
63873 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
63874 var points = nodes.map(function (node) {
63875 return context.projection(node.loc);
63877 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) return [];
63878 var autoArgs; // don't allow autosquaring features linked to wikidata
63880 if (!entity.tags.wikidata) {
63881 // use same degree threshold as for detection
63882 var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
63883 autoAction.transitionable = false; // when autofixing, do it instantly
63885 autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature', {
63890 return [new validationIssue({
63892 subtype: 'building',
63893 severity: 'warning',
63894 message: function message(context) {
63895 var entity = context.hasEntity(this.entityIds[0]);
63896 return entity ? _t.html('issues.unsquare_way.message', {
63897 feature: utilDisplayLabel(entity, context.graph())
63900 reference: showReference,
63901 entityIds: [entity.id],
63902 hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
63903 dynamicFixes: function dynamicFixes() {
63904 return [new validationIssueFix({
63905 icon: 'iD-operation-orthogonalize',
63906 title: _t.html('issues.fix.square_feature.title'),
63907 autoArgs: autoArgs,
63908 onClick: function onClick(context, completionHandler) {
63909 var entityId = this.issue.entityIds[0]; // use same degree threshold as for detection
63911 context.perform(actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold), _t('operations.orthogonalize.annotation.feature', {
63913 })); // run after the squaring transition (currently 150ms)
63915 window.setTimeout(function () {
63916 completionHandler();
63921 new validationIssueFix({
63922 title: t.html('issues.fix.tag_as_unsquare.title'),
63923 onClick: function(context) {
63924 var entityId = this.issue.entityIds[0];
63925 var entity = context.entity(entityId);
63926 var tags = Object.assign({}, entity.tags); // shallow copy
63927 tags.nonsquare = 'yes';
63929 actionChangeTags(entityId, tags),
63930 t('issues.fix.tag_as_unsquare.annotation')
63939 function showReference(selection) {
63940 selection.selectAll('.issue-reference').data([0]).enter().append('div').attr('class', 'issue-reference').html(_t.html('issues.unsquare_way.buildings.reference'));
63944 validation.type = type;
63948 var Validations = /*#__PURE__*/Object.freeze({
63950 validationAlmostJunction: validationAlmostJunction,
63951 validationCloseNodes: validationCloseNodes,
63952 validationCrossingWays: validationCrossingWays,
63953 validationDisconnectedWay: validationDisconnectedWay,
63954 validationFormatting: validationFormatting,
63955 validationHelpRequest: validationHelpRequest,
63956 validationImpossibleOneway: validationImpossibleOneway,
63957 validationIncompatibleSource: validationIncompatibleSource,
63958 validationMaprules: validationMaprules,
63959 validationMismatchedGeometry: validationMismatchedGeometry,
63960 validationMissingRole: validationMissingRole,
63961 validationMissingTag: validationMissingTag,
63962 validationOutdatedTags: validationOutdatedTags,
63963 validationPrivateData: validationPrivateData,
63964 validationSuspiciousName: validationSuspiciousName,
63965 validationUnsquareWay: validationUnsquareWay
63968 function coreValidator(context) {
63969 var dispatch$1 = dispatch('validated', 'focusedIssue');
63970 var validator = utilRebind({}, dispatch$1, 'on');
63972 var _disabledRules = {};
63973 var _ignoredIssueIDs = {}; // issue.id -> true
63975 var _baseCache = validationCache(); // issues before any user edits
63978 var _headCache = validationCache(); // issues after all user edits
63981 var _validatedGraph = null;
63983 var _deferred = new Set(); //
63984 // initialize the validator rulesets
63988 validator.init = function () {
63989 Object.values(Validations).forEach(function (validation) {
63990 if (typeof validation !== 'function') return;
63991 var fn = validation(context);
63995 var disabledRules = corePreferences('validate-disabledRules');
63997 if (disabledRules) {
63998 disabledRules.split(',').forEach(function (key) {
63999 _disabledRules[key] = true;
64004 function reset(resetIgnored) {
64005 Array.from(_deferred).forEach(function (handle) {
64006 window.cancelIdleCallback(handle);
64008 _deferred["delete"](handle);
64009 }); // clear caches
64011 if (resetIgnored) _ignoredIssueIDs = {};
64012 _baseCache = validationCache();
64013 _headCache = validationCache();
64014 _validatedGraph = null;
64016 // clear caches, called whenever iD resets after a save
64020 validator.reset = function () {
64024 validator.resetIgnoredIssues = function () {
64025 _ignoredIssueIDs = {}; // reload UI
64027 dispatch$1.call('validated');
64028 }; // must update issues when the user changes the unsquare thereshold
64031 validator.reloadUnsquareIssues = function () {
64032 reloadUnsquareIssues(_headCache, context.graph());
64033 reloadUnsquareIssues(_baseCache, context.history().base());
64034 dispatch$1.call('validated');
64037 function reloadUnsquareIssues(cache, graph) {
64038 var checkUnsquareWay = _rules.unsquare_way;
64039 if (typeof checkUnsquareWay !== 'function') return; // uncache existing
64041 cache.uncacheIssuesOfType('unsquare_way');
64042 var buildings = context.history().tree().intersects(geoExtent([-180, -90], [180, 90]), graph) // everywhere
64043 .filter(function (entity) {
64044 return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
64045 }); // rerun for all buildings
64047 buildings.forEach(function (entity) {
64048 var detected = checkUnsquareWay(entity, graph);
64049 if (detected.length !== 1) return;
64050 var issue = detected[0];
64052 if (!cache.issuesByEntityID[entity.id]) {
64053 cache.issuesByEntityID[entity.id] = new Set();
64056 cache.issuesByEntityID[entity.id].add(issue.id);
64057 cache.issuesByIssueID[issue.id] = issue;
64060 // what: 'all', // 'all' or 'edited'
64061 // where: 'all', // 'all' or 'visible'
64062 // includeIgnored: false // true, false, or 'only'
64063 // includeDisabledRules: false // true, false, or 'only'
64067 validator.getIssues = function (options) {
64068 var opts = Object.assign({
64071 includeIgnored: false,
64072 includeDisabledRules: false
64074 var issues = Object.values(_headCache.issuesByIssueID);
64075 var view = context.map().extent();
64076 return issues.filter(function (issue) {
64077 if (!issue) return false;
64078 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
64079 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
64080 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
64081 if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false; // Sanity check: This issue may be for an entity that not longer exists.
64082 // If we detect this, uncache and return false so it is not included..
64084 var entityIds = issue.entityIds || [];
64086 for (var i = 0; i < entityIds.length; i++) {
64087 var entityId = entityIds[i];
64089 if (!context.hasEntity(entityId)) {
64090 delete _headCache.issuesByEntityID[entityId];
64091 delete _headCache.issuesByIssueID[issue.id];
64096 if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) return false;
64098 if (opts.where === 'visible') {
64099 var extent = issue.extent(context.graph());
64100 if (!view.intersects(extent)) return false;
64107 validator.getResolvedIssues = function () {
64108 var baseIssues = Object.values(_baseCache.issuesByIssueID);
64109 return baseIssues.filter(function (issue) {
64110 return !_headCache.issuesByIssueID[issue.id];
64114 validator.focusIssue = function (issue) {
64115 var extent = issue.extent(context.graph());
64118 var setZoom = Math.max(context.map().zoom(), 19);
64119 context.map().unobscuredCenterZoomEase(extent.center(), setZoom); // select the first entity
64121 if (issue.entityIds && issue.entityIds.length) {
64122 window.setTimeout(function () {
64123 var ids = issue.entityIds;
64124 context.enter(modeSelect(context, [ids[0]]));
64125 dispatch$1.call('focusedIssue', this, issue);
64126 }, 250); // after ease
64131 validator.getIssuesBySeverity = function (options) {
64132 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
64133 groups.error = groups.error || [];
64134 groups.warning = groups.warning || [];
64136 }; // show some issue types in a particular order
64139 var orderedIssueTypes = [// flag missing data first
64140 'missing_tag', 'missing_role', // then flag identity issues
64141 'outdated_tags', 'mismatched_geometry', // flag geometry issues where fixing them might solve connectivity issues
64142 'crossing_ways', 'almost_junction', // then flag connectivity issues
64143 'disconnected_way', 'impossible_oneway']; // returns the issues that the given entity IDs have in common, matching the given options
64145 validator.getSharedEntityIssues = function (entityIDs, options) {
64146 var cache = _headCache; // gather the issues that are common to all the entities
64148 var issueIDs = entityIDs.reduce(function (acc, entityID) {
64149 var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
64152 return new Set(entityIssueIDs);
64155 return new Set(_toConsumableArray(acc).filter(function (elem) {
64156 return entityIssueIDs.has(elem);
64159 var opts = options || {};
64160 return Array.from(issueIDs).map(function (id) {
64161 return cache.issuesByIssueID[id];
64162 }).filter(function (issue) {
64163 if (!issue) return false;
64164 if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) return false;
64165 if (!opts.includeDisabledRules && _disabledRules[issue.type]) return false;
64166 if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) return false;
64167 if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) return false;
64169 }).sort(function (issue1, issue2) {
64170 if (issue1.type === issue2.type) {
64171 // issues of the same type, sort deterministically
64172 return issue1.id < issue2.id ? -1 : 1;
64175 var index1 = orderedIssueTypes.indexOf(issue1.type);
64176 var index2 = orderedIssueTypes.indexOf(issue2.type);
64178 if (index1 !== -1 && index2 !== -1) {
64179 // both issue types have explicit sort orders
64180 return index1 - index2;
64181 } else if (index1 === -1 && index2 === -1) {
64182 // neither issue type has an explicit sort order, sort by type
64183 return issue1.type < issue2.type ? -1 : 1;
64185 // order explicit types before everything else
64186 return index1 !== -1 ? -1 : 1;
64191 validator.getEntityIssues = function (entityID, options) {
64192 return validator.getSharedEntityIssues([entityID], options);
64195 validator.getRuleKeys = function () {
64196 return Object.keys(_rules);
64199 validator.isRuleEnabled = function (key) {
64200 return !_disabledRules[key];
64203 validator.toggleRule = function (key) {
64204 if (_disabledRules[key]) {
64205 delete _disabledRules[key];
64207 _disabledRules[key] = true;
64210 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
64211 validator.validate();
64214 validator.disableRules = function (keys) {
64215 _disabledRules = {};
64216 keys.forEach(function (k) {
64217 _disabledRules[k] = true;
64219 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
64220 validator.validate();
64223 validator.ignoreIssue = function (id) {
64224 _ignoredIssueIDs[id] = true;
64226 // Run validation on a single entity for the given graph
64230 function validateEntity(entity, graph) {
64231 var entityIssues = []; // runs validation and appends resulting issues
64233 function runValidation(key) {
64234 var fn = _rules[key];
64236 if (typeof fn !== 'function') {
64237 console.error('no such validation rule = ' + key); // eslint-disable-line no-console
64242 var detected = fn(entity, graph);
64243 entityIssues = entityIssues.concat(detected);
64247 Object.keys(_rules).forEach(runValidation);
64248 return entityIssues;
64251 function entityIDsToValidate(entityIDs, graph) {
64252 var processedIDs = new Set();
64253 return entityIDs.reduce(function (acc, entityID) {
64254 // keep redundancy check separate from `acc` because an `entityID`
64255 // could have been added to `acc` as a related entity through an earlier pass
64256 if (processedIDs.has(entityID)) return acc;
64257 processedIDs.add(entityID);
64258 var entity = graph.hasEntity(entityID);
64259 if (!entity) return acc;
64261 var checkParentRels = [entity];
64263 if (entity.type === 'node') {
64264 graph.parentWays(entity).forEach(function (parentWay) {
64265 acc.add(parentWay.id); // include parent ways
64267 checkParentRels.push(parentWay);
64269 } else if (entity.type === 'relation') {
64270 entity.members.forEach(function (member) {
64271 acc.add(member.id); // include members
64273 } else if (entity.type === 'way') {
64274 entity.nodes.forEach(function (nodeID) {
64275 acc.add(nodeID); // include child nodes
64277 graph._parentWays[nodeID].forEach(function (wayID) {
64278 acc.add(wayID); // include connected ways
64283 checkParentRels.forEach(function (entity) {
64284 // include parent relations
64285 if (entity.type !== 'relation') {
64286 // but not super-relations
64287 graph.parentRelations(entity).forEach(function (parentRelation) {
64288 acc.add(parentRelation.id);
64295 // Run validation for several entities, supplied `entityIDs`,
64296 // against `graph` for the given `cache`
64300 function validateEntities(entityIDs, graph, cache) {
64301 // clear caches for existing issues related to these entities
64302 entityIDs.forEach(cache.uncacheEntityID); // detect new issues and update caches
64304 entityIDs.forEach(function (entityID) {
64305 var entity = graph.hasEntity(entityID); // don't validate deleted entities
64307 if (!entity) return;
64308 var issues = validateEntity(entity, graph);
64309 cache.cacheIssues(issues);
64312 // Validates anything that has changed since the last time it was run.
64313 // Also updates the "validatedGraph" to be the current graph
64314 // and dispatches a `validated` event when finished.
64318 validator.validate = function () {
64319 var currGraph = context.graph();
64320 _validatedGraph = _validatedGraph || context.history().base();
64322 if (currGraph === _validatedGraph) {
64323 dispatch$1.call('validated');
64327 var oldGraph = _validatedGraph;
64328 var difference = coreDifference(oldGraph, currGraph);
64329 _validatedGraph = currGraph;
64330 var createdAndModifiedEntityIDs = difference.extantIDs(true); // created/modified (true = w/relation members)
64332 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph); // check modified and deleted entities against the old graph in order to update their related entities
64333 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
64335 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified()).map(function (entity) {
64338 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph); // concat the sets
64340 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
64341 validateEntities(entityIDsToCheck, context.graph(), _headCache);
64342 dispatch$1.call('validated');
64345 context.history().on('reset.validator', function () {
64346 // cached issues aren't valid any longer if the history has been reset
64348 validator.validate();
64349 }); // WHEN TO RUN VALIDATION:
64350 // When graph changes:
64352 context.history().on('restore.validator', validator.validate) // restore saved history
64353 .on('undone.validator', validator.validate) // undo
64354 .on('redone.validator', validator.validate); // redo
64355 // but not on 'change' (e.g. while drawing)
64356 // When user changes editing modes:
64358 context.on('exit.validator', validator.validate); // When merging fetched data:
64360 context.history().on('merge.validator', function (entities) {
64361 if (!entities) return;
64362 var handle = window.requestIdleCallback(function () {
64363 var entityIDs = entities.map(function (entity) {
64366 var headGraph = context.graph();
64367 validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
64368 var baseGraph = context.history().base();
64369 validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
64370 dispatch$1.call('validated');
64373 _deferred.add(handle);
64378 function validationCache() {
64380 issuesByIssueID: {},
64381 // issue.id -> issue
64382 issuesByEntityID: {} // entity.id -> set(issue.id)
64386 cache.cacheIssues = function (issues) {
64387 issues.forEach(function (issue) {
64388 var entityIds = issue.entityIds || [];
64389 entityIds.forEach(function (entityId) {
64390 if (!cache.issuesByEntityID[entityId]) {
64391 cache.issuesByEntityID[entityId] = new Set();
64394 cache.issuesByEntityID[entityId].add(issue.id);
64396 cache.issuesByIssueID[issue.id] = issue;
64400 cache.uncacheIssue = function (issue) {
64401 // When multiple entities are involved (e.g. crossing_ways),
64402 // remove this issue from the other entity caches too..
64403 var entityIds = issue.entityIds || [];
64404 entityIds.forEach(function (entityId) {
64405 if (cache.issuesByEntityID[entityId]) {
64406 cache.issuesByEntityID[entityId]["delete"](issue.id);
64409 delete cache.issuesByIssueID[issue.id];
64412 cache.uncacheIssues = function (issues) {
64413 issues.forEach(cache.uncacheIssue);
64416 cache.uncacheIssuesOfType = function (type) {
64417 var issuesOfType = Object.values(cache.issuesByIssueID).filter(function (issue) {
64418 return issue.type === type;
64420 cache.uncacheIssues(issuesOfType);
64422 // Remove a single entity and all its related issues from the caches
64426 cache.uncacheEntityID = function (entityID) {
64427 var issueIDs = cache.issuesByEntityID[entityID];
64428 if (!issueIDs) return;
64429 issueIDs.forEach(function (issueID) {
64430 var issue = cache.issuesByIssueID[issueID];
64433 cache.uncacheIssue(issue);
64435 delete cache.issuesByIssueID[issueID];
64438 delete cache.issuesByEntityID[entityID];
64444 function coreUploader(context) {
64445 var dispatch$1 = dispatch( // Start and end events are dispatched exactly once each per legitimate outside call to `save`
64446 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
64447 'saveEnded', // dispatched after the result event has been dispatched
64448 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
64449 'progressChanged', // Each save results in one of these outcomes:
64450 'resultNoChanges', // upload wasn't attempted since there were no edits
64451 'resultErrors', // upload failed due to errors
64452 'resultConflicts', // upload failed due to data conflicts
64453 'resultSuccess' // upload completed without errors
64455 var _isSaving = false;
64456 var _conflicts = [];
64461 var _discardTags = {};
64462 _mainFileFetcher.get('discarded').then(function (d) {
64464 })["catch"](function () {
64467 var uploader = utilRebind({}, dispatch$1, 'on');
64469 uploader.isSaving = function () {
64473 uploader.save = function (changeset, tryAgain, checkConflicts) {
64474 // Guard against accidentally entering save code twice - #4641
64475 if (_isSaving && !tryAgain) {
64479 var osm = context.connection();
64480 if (!osm) return; // If user somehow got logged out mid-save, try to reauthenticate..
64481 // This can happen if they were logged in from before, but the tokens are no longer valid.
64483 if (!osm.authenticated()) {
64484 osm.authenticate(function (err) {
64486 uploader.save(changeset, tryAgain, checkConflicts); // continue where we left off..
64494 dispatch$1.call('saveStarted', this);
64497 var history = context.history();
64499 _errors = []; // Store original changes, in case user wants to download them as an .osc file
64501 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags)); // First time, `history.perform` a no-op action.
64502 // Any conflict resolutions will be done as `history.replace`
64503 // Remember to pop this later if needed
64506 history.perform(actionNoop());
64507 } // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
64510 if (!checkConflicts) {
64511 upload(changeset); // Do the full (slow) conflict check..
64513 performFullConflictCheck(changeset);
64517 function performFullConflictCheck(changeset) {
64518 var osm = context.connection();
64520 var history = context.history();
64521 var localGraph = context.graph();
64522 var remoteGraph = coreGraph(history.base(), true);
64523 var summary = history.difference().summary();
64526 for (var i = 0; i < summary.length; i++) {
64527 var item = summary[i];
64529 if (item.changeType === 'modified') {
64530 _toCheck.push(item.entity.id);
64534 var _toLoad = withChildNodes(_toCheck, localGraph);
64537 var _toLoadCount = 0;
64538 var _toLoadTotal = _toLoad.length;
64540 if (_toCheck.length) {
64541 dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
64543 _toLoad.forEach(function (id) {
64544 _loaded[id] = false;
64547 osm.loadMultiple(_toLoad, loaded);
64554 function withChildNodes(ids, graph) {
64555 var s = new Set(ids);
64556 ids.forEach(function (id) {
64557 var entity = graph.entity(id);
64558 if (entity.type !== 'way') return;
64559 graph.childNodes(entity).forEach(function (child) {
64560 if (child.version !== undefined) {
64565 return Array.from(s);
64566 } // Reload modified entities into an alternate graph and check for conflicts..
64569 function loaded(err, result) {
64570 if (_errors.length) return;
64574 msg: err.message || err.responseText,
64575 details: [_t('save.status_code', {
64580 didResultInErrors();
64583 result.data.forEach(function (entity) {
64584 remoteGraph.replace(entity);
64585 _loaded[entity.id] = true;
64586 _toLoad = _toLoad.filter(function (val) {
64587 return val !== entity.id;
64589 if (!entity.visible) return; // Because loadMultiple doesn't download /full like loadEntity,
64590 // need to also load children that aren't already being checked..
64594 if (entity.type === 'way') {
64595 for (i = 0; i < entity.nodes.length; i++) {
64596 id = entity.nodes[i];
64598 if (_loaded[id] === undefined) {
64599 _loaded[id] = false;
64603 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
64604 for (i = 0; i < entity.members.length; i++) {
64605 id = entity.members[i].id;
64607 if (_loaded[id] === undefined) {
64608 _loaded[id] = false;
64614 _toLoadCount += result.data.length;
64615 _toLoadTotal += loadMore.length;
64616 dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
64618 if (loadMore.length) {
64619 _toLoad.push.apply(_toLoad, loadMore);
64621 osm.loadMultiple(loadMore, loaded);
64624 if (!_toLoad.length) {
64631 function detectConflicts() {
64632 function choice(id, text, _action) {
64636 action: function action() {
64637 history.replace(_action);
64642 function formatUser(d) {
64643 return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
64646 function entityName(entity) {
64647 return utilDisplayName(entity) || utilDisplayType(entity.id) + ' ' + entity.id;
64650 function sameVersions(local, remote) {
64651 if (local.version !== remote.version) return false;
64653 if (local.type === 'way') {
64654 var children = utilArrayUnion(local.nodes, remote.nodes);
64656 for (var i = 0; i < children.length; i++) {
64657 var a = localGraph.hasEntity(children[i]);
64658 var b = remoteGraph.hasEntity(children[i]);
64659 if (a && b && a.version !== b.version) return false;
64666 _toCheck.forEach(function (id) {
64667 var local = localGraph.entity(id);
64668 var remote = remoteGraph.entity(id);
64669 if (sameVersions(local, remote)) return;
64670 var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
64671 history.replace(merge);
64672 var mergeConflicts = merge.conflicts();
64673 if (!mergeConflicts.length) return; // merged safely
64675 var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
64676 var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
64677 var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
64678 var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
64682 name: entityName(local),
64683 details: mergeConflicts,
64685 choices: [choice(id, keepMine, forceLocal), choice(id, keepTheirs, forceRemote)]
64691 function upload(changeset) {
64692 var osm = context.connection();
64696 msg: 'No OSM Service'
64700 if (_conflicts.length) {
64701 didResultInConflicts(changeset);
64702 } else if (_errors.length) {
64703 didResultInErrors();
64705 var history = context.history();
64706 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
64708 if (changes.modified.length || changes.created.length || changes.deleted.length) {
64709 dispatch$1.call('willAttemptUpload', this);
64710 osm.putChangeset(changeset, changes, uploadCallback);
64712 // changes were insignificant or reverted by user
64713 didResultInNoChanges();
64718 function uploadCallback(err, changeset) {
64720 if (err.status === 409) {
64722 uploader.save(changeset, true, true); // tryAgain = true, checkConflicts = true
64725 msg: err.message || err.responseText,
64726 details: [_t('save.status_code', {
64731 didResultInErrors();
64734 didResultInSuccess(changeset);
64738 function didResultInNoChanges() {
64739 dispatch$1.call('resultNoChanges', this);
64741 context.flush(); // reset iD
64744 function didResultInErrors() {
64745 context.history().pop();
64746 dispatch$1.call('resultErrors', this, _errors);
64750 function didResultInConflicts(changeset) {
64751 _conflicts.sort(function (a, b) {
64752 return b.id.localeCompare(a.id);
64755 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
64759 function didResultInSuccess(changeset) {
64760 // delete the edit stack cached to local storage
64761 context.history().clearSaved();
64762 dispatch$1.call('resultSuccess', this, changeset); // Add delay to allow for postgres replication #1646 #2678
64764 window.setTimeout(function () {
64766 context.flush(); // reset iD
64770 function endSave() {
64772 dispatch$1.call('saveEnded', this);
64775 uploader.cancelConflictResolution = function () {
64776 context.history().pop();
64779 uploader.processResolvedConflicts = function (changeset) {
64780 var history = context.history();
64782 for (var i = 0; i < _conflicts.length; i++) {
64783 if (_conflicts[i].chosen === 1) {
64784 // user chose "use theirs"
64785 var entity = context.hasEntity(_conflicts[i].id);
64787 if (entity && entity.type === 'way') {
64788 var children = utilArrayUniq(entity.nodes);
64790 for (var j = 0; j < children.length; j++) {
64791 history.replace(actionRevert(children[j]));
64795 history.replace(actionRevert(_conflicts[i].id));
64799 uploader.save(changeset, true, false); // tryAgain = true, checkConflicts = false
64802 uploader.reset = function () {};
64807 var abs$4 = Math.abs;
64808 var exp$2 = Math.exp;
64811 var FORCED$g = fails(function () {
64812 return Math.sinh(-2e-17) != -2e-17;
64815 // `Math.sinh` method
64816 // https://tc39.github.io/ecma262/#sec-math.sinh
64817 // V8 near Chromium 38 has a problem with very small numbers
64818 _export({ target: 'Math', stat: true, forced: FORCED$g }, {
64819 sinh: function sinh(x) {
64820 return abs$4(x = +x) < 1 ? (mathExpm1(x) - mathExpm1(-x)) / 2 : (exp$2(x - 1) - exp$2(-x - 1)) * (E / 2);
64824 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
64826 window.matchMedia("\n (-webkit-min-device-pixel-ratio: 2), /* Safari */\n (min-resolution: 2dppx), /* standard */\n (min-resolution: 192dpi) /* fallback */\n ").addListener(function () {
64827 isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
64830 function localeDateString(s) {
64831 if (!s) return null;
64837 var d = new Date(s);
64838 if (isNaN(d.getTime())) return null;
64839 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
64842 function vintageRange(vintage) {
64845 if (vintage.start || vintage.end) {
64846 s = vintage.start || '?';
64848 if (vintage.start !== vintage.end) {
64849 s += ' - ' + (vintage.end || '?');
64856 function rendererBackgroundSource(data) {
64857 var source = Object.assign({}, data); // shallow copy
64859 var _offset = [0, 0];
64860 var _name = source.name;
64861 var _description = source.description;
64863 var _best = !!source.best;
64865 var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
64867 source.tileSize = data.tileSize || 256;
64868 source.zoomExtent = data.zoomExtent || [0, 22];
64869 source.overzoom = data.overzoom !== false;
64871 source.offset = function (val) {
64872 if (!arguments.length) return _offset;
64877 source.nudge = function (val, zoomlevel) {
64878 _offset[0] += val[0] / Math.pow(2, zoomlevel);
64879 _offset[1] += val[1] / Math.pow(2, zoomlevel);
64883 source.name = function () {
64884 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64885 return _t('imagery.' + id_safe + '.name', {
64890 source.label = function () {
64891 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64892 return _t.html('imagery.' + id_safe + '.name', {
64897 source.description = function () {
64898 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
64899 return _t.html('imagery.' + id_safe + '.description', {
64900 "default": _description
64904 source.best = function () {
64908 source.area = function () {
64909 if (!data.polygon) return Number.MAX_VALUE; // worldwide
64911 var area = d3_geoArea({
64912 type: 'MultiPolygon',
64913 coordinates: [data.polygon]
64915 return isNaN(area) ? 0 : area;
64918 source.imageryUsed = function () {
64919 return _name || source.id;
64922 source.template = function (val) {
64923 if (!arguments.length) return _template;
64925 if (source.id === 'custom') {
64932 source.url = function (coord) {
64933 var result = _template;
64934 if (result === '') return result; // source 'none'
64935 // Guess a type based on the tokens present in the template
64936 // (This is for 'custom' source, where we don't know)
64938 if (!source.type) {
64939 if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
64940 source.type = 'wms';
64941 source.projection = 'EPSG:3857'; // guess
64942 } else if (/\{(x|y)\}/.test(_template)) {
64943 source.type = 'tms';
64944 } else if (/\{u\}/.test(_template)) {
64945 source.type = 'bing';
64949 if (source.type === 'wms') {
64950 var tileToProjectedCoords = function tileToProjectedCoords(x, y, z) {
64951 //polyfill for IE11, PhantomJS
64952 var sinh = Math.sinh || function (x) {
64953 var y = Math.exp(x);
64954 return (y - 1 / y) / 2;
64957 var zoomSize = Math.pow(2, z);
64958 var lon = x / zoomSize * Math.PI * 2 - Math.PI;
64959 var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
64961 switch (source.projection) {
64964 x: lon * 180 / Math.PI,
64965 y: lat * 180 / Math.PI
64969 // EPSG:3857 and synonyms
64970 var mercCoords = mercatorRaw(lon, lat);
64972 x: 20037508.34 / Math.PI * mercCoords[0],
64973 y: 20037508.34 / Math.PI * mercCoords[1]
64978 var tileSize = source.tileSize;
64979 var projection = source.projection;
64980 var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
64981 var maxXminY = tileToProjectedCoords(coord[0] + 1, coord[1] + 1, coord[2]);
64982 result = result.replace(/\{(\w+)\}/g, function (token, key) {
64992 return projection.replace(/^EPSG:/, '');
64995 // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
64996 if (projection === 'EPSG:4326' && // The CRS parameter implies version 1.3 (prior versions use SRS)
64997 /VERSION=1.3|CRS={proj}/.test(source.template())) {
64998 return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
65000 return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
65019 } else if (source.type === 'tms') {
65020 result = result.replace('{x}', coord[0]).replace('{y}', coord[1]) // TMS-flipped y coordinate
65021 .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1).replace(/\{z(oom)?\}/, coord[2]) // only fetch retina tiles for retina screens
65022 .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
65023 } else if (source.type === 'bing') {
65024 result = result.replace('{u}', function () {
65027 for (var zoom = coord[2]; zoom > 0; zoom--) {
65029 var mask = 1 << zoom - 1;
65030 if ((coord[0] & mask) !== 0) b++;
65031 if ((coord[1] & mask) !== 0) b += 2;
65037 } // these apply to any type..
65040 result = result.replace(/\{switch:([^}]+)\}/, function (s, r) {
65041 var subdomains = r.split(',');
65042 return subdomains[(coord[0] + coord[1]) % subdomains.length];
65047 source.validZoom = function (z) {
65048 return source.zoomExtent[0] <= z && (source.overzoom || source.zoomExtent[1] > z);
65051 source.isLocatorOverlay = function () {
65052 return source.id === 'mapbox_locator_overlay';
65054 /* hides a source from the list, but leaves it available for use */
65057 source.isHidden = function () {
65058 return source.id === 'DigitalGlobe-Premium-vintage' || source.id === 'DigitalGlobe-Standard-vintage';
65061 source.copyrightNotices = function () {};
65063 source.getMetadata = function (center, tileCoord, callback) {
65065 start: localeDateString(source.startDate),
65066 end: localeDateString(source.endDate)
65068 vintage.range = vintageRange(vintage);
65072 callback(null, metadata);
65078 rendererBackgroundSource.Bing = function (data, dispatch) {
65079 // http://msdn.microsoft.com/en-us/library/ff701716.aspx
65080 // http://msdn.microsoft.com/en-us/library/ff701701.aspx
65081 data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
65082 var bing = rendererBackgroundSource(data); // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
65084 var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q'; // iD
65086 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
65089 var providers = [];
65090 d3_json(url).then(function (json) {
65091 providers = json.resourceSets[0].resources[0].imageryProviders.map(function (provider) {
65093 attribution: provider.attribution,
65094 areas: provider.coverageAreas.map(function (area) {
65096 zoom: [area.zoomMin, area.zoomMax],
65097 extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
65102 dispatch.call('change');
65103 })["catch"](function () {
65107 bing.copyrightNotices = function (zoom, extent) {
65108 zoom = Math.min(zoom, 21);
65109 return providers.filter(function (provider) {
65110 return provider.areas.some(function (area) {
65111 return extent.intersects(area.extent) && area.zoom[0] <= zoom && area.zoom[1] >= zoom;
65113 }).map(function (provider) {
65114 return provider.attribution;
65118 bing.getMetadata = function (center, tileCoord, callback) {
65119 var tileID = tileCoord.slice(0, 3).join('/');
65120 var zoom = Math.min(tileCoord[2], 21);
65121 var centerPoint = center[1] + ',' + center[0]; // lat,lng
65123 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint + '?zl=' + zoom + '&key=' + key;
65124 if (inflight[tileID]) return;
65126 if (!cache[tileID]) {
65127 cache[tileID] = {};
65130 if (cache[tileID] && cache[tileID].metadata) {
65131 return callback(null, cache[tileID].metadata);
65134 inflight[tileID] = true;
65135 d3_json(url).then(function (result) {
65136 delete inflight[tileID];
65139 throw new Error('Unknown Error');
65143 start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
65144 end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
65146 vintage.range = vintageRange(vintage);
65150 cache[tileID].metadata = metadata;
65151 if (callback) callback(null, metadata);
65152 })["catch"](function (err) {
65153 delete inflight[tileID];
65154 if (callback) callback(err.message);
65158 bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
65162 rendererBackgroundSource.Esri = function (data) {
65163 // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
65164 if (data.template.match(/blankTile/) === null) {
65165 data.template = data.template + '?blankTile=false';
65168 var esri = rendererBackgroundSource(data);
65172 var _prevCenter; // use a tilemap service to set maximum zoom for esri tiles dynamically
65173 // https://developers.arcgis.com/documentation/tiled-elevation-service/
65176 esri.fetchTilemap = function (center) {
65177 // skip if we have already fetched a tilemap within 5km
65178 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) return;
65179 _prevCenter = center; // tiles are available globally to zoom level 19, afterward they may or may not be present
65181 var z = 20; // first generate a random url using the template
65183 var dummyUrl = esri.url([1, 2, 3]); // calculate url z/y/x from the lat/long of the center of the map
65185 var x = Math.floor((center[0] + 180) / 360 * Math.pow(2, z));
65186 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
65188 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
65190 d3_json(tilemapUrl).then(function (tilemap) {
65192 throw new Error('Unknown Error');
65195 var hasTiles = true;
65197 for (var i = 0; i < tilemap.data.length; i++) {
65198 // 0 means an individual tile in the grid doesn't exist
65199 if (!tilemap.data[i]) {
65203 } // if any tiles are missing at level 20 we restrict maxZoom to 19
65206 esri.zoomExtent[1] = hasTiles ? 22 : 19;
65207 })["catch"](function () {
65212 esri.getMetadata = function (center, tileCoord, callback) {
65213 var tileID = tileCoord.slice(0, 3).join('/');
65214 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
65215 var centerPoint = center[0] + ',' + center[1]; // long, lat (as it should be)
65217 var unknown = _t('info_panels.background.unknown');
65221 if (inflight[tileID]) return;
65224 case zoom >= 20 && esri.id === 'EsriWorldImageryClarity':
65241 metadataLayer = 99;
65244 var url; // build up query using the layer appropriate to the current zoom
65246 if (esri.id === 'EsriWorldImagery') {
65247 url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
65248 } else if (esri.id === 'EsriWorldImageryClarity') {
65249 url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
65252 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
65254 if (!cache[tileID]) {
65255 cache[tileID] = {};
65258 if (cache[tileID] && cache[tileID].metadata) {
65259 return callback(null, cache[tileID].metadata);
65260 } // accurate metadata is only available >= 13
65263 if (metadataLayer === 99) {
65272 description: unknown,
65273 resolution: unknown,
65276 callback(null, metadata);
65278 inflight[tileID] = true;
65279 d3_json(url).then(function (result) {
65280 delete inflight[tileID];
65283 throw new Error('Unknown Error');
65284 } else if (result.features && result.features.length < 1) {
65285 throw new Error('No Results');
65286 } else if (result.error && result.error.message) {
65287 throw new Error(result.error.message);
65288 } // pass through the discrete capture date from metadata
65291 var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
65293 start: captureDate,
65299 source: clean(result.features[0].attributes.NICE_NAME),
65300 description: clean(result.features[0].attributes.NICE_DESC),
65301 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
65302 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
65303 }; // append units - meters
65305 if (isFinite(metadata.resolution)) {
65306 metadata.resolution += ' m';
65309 if (isFinite(metadata.accuracy)) {
65310 metadata.accuracy += ' m';
65313 cache[tileID].metadata = metadata;
65314 if (callback) callback(null, metadata);
65315 })["catch"](function (err) {
65316 delete inflight[tileID];
65317 if (callback) callback(err.message);
65321 function clean(val) {
65322 return String(val).trim() || unknown;
65329 rendererBackgroundSource.None = function () {
65330 var source = rendererBackgroundSource({
65335 source.name = function () {
65336 return _t('background.none');
65339 source.label = function () {
65340 return _t.html('background.none');
65343 source.imageryUsed = function () {
65347 source.area = function () {
65348 return -1; // sources in background pane are sorted by area
65354 rendererBackgroundSource.Custom = function (template) {
65355 var source = rendererBackgroundSource({
65360 source.name = function () {
65361 return _t('background.custom');
65364 source.label = function () {
65365 return _t.html('background.custom');
65368 source.imageryUsed = function () {
65369 // sanitize personal connection tokens - #6801
65370 var cleaned = source.template(); // from query string parameters
65372 if (cleaned.indexOf('?') !== -1) {
65373 var parts = cleaned.split('?', 2);
65374 var qs = utilStringQs(parts[1]);
65375 ['access_token', 'connectId', 'token'].forEach(function (param) {
65377 qs[param] = '{apikey}';
65380 cleaned = parts[0] + '?' + utilQsString(qs, true); // true = soft encode
65381 } // from wms/wmts api path parameters
65384 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
65385 return 'Custom (' + cleaned + ' )';
65388 source.area = function () {
65389 return -2; // sources in background pane are sorted by area
65395 function rendererTileLayer(context) {
65396 var transformProp = utilPrefixCSSProperty('Transform');
65397 var tiler = utilTiler();
65398 var _tileSize = 256;
65410 function tileSizeAtZoom(d, z) {
65411 var EPSILON = 0.002; // close seams
65413 return _tileSize * Math.pow(2, z - d[2]) / _tileSize + EPSILON;
65416 function atZoom(t, distance) {
65417 var power = Math.pow(2, distance);
65418 return [Math.floor(t[0] * power), Math.floor(t[1] * power), t[2] + distance];
65421 function lookUp(d) {
65422 for (var up = -1; up > -d[2]; up--) {
65423 var tile = atZoom(d, up);
65425 if (_cache[_source.url(tile)] !== false) {
65431 function uniqueBy(a, n) {
65435 for (var i = 0; i < a.length; i++) {
65436 if (seen[a[i][n]] === undefined) {
65438 seen[a[i][n]] = true;
65445 function addSource(d) {
65446 d.push(_source.url(d));
65448 } // Update tiles based on current state of `projection`.
65451 function background(selection) {
65452 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
65456 pixelOffset = [_source.offset()[0] * Math.pow(2, _zoom), _source.offset()[1] * Math.pow(2, _zoom)];
65458 pixelOffset = [0, 0];
65461 var translate = [_projection.translate()[0] + pixelOffset[0], _projection.translate()[1] + pixelOffset[1]];
65462 tiler.scale(_projection.scale() * 2 * Math.PI).translate(translate);
65463 _tileOrigin = [_projection.scale() * Math.PI - translate[0], _projection.scale() * Math.PI - translate[1]];
65465 } // Derive the tiles onscreen, remove those offscreen and position them.
65466 // Important that this part not depend on `_projection` because it's
65467 // rentered when tiles load/error (see #644).
65470 function render(selection) {
65471 if (!_source) return;
65473 var showDebug = context.getDebug('tile') && !_source.overlay;
65475 if (_source.validZoom(_zoom)) {
65476 tiler.skipNullIsland(!!_source.overlay);
65477 tiler().forEach(function (d) {
65479 if (d[3] === '') return;
65480 if (typeof d[3] !== 'string') return; // Workaround for #2295
65484 if (_cache[d[3]] === false && lookUp(d)) {
65485 requests.push(addSource(lookUp(d)));
65488 requests = uniqueBy(requests, 3).filter(function (r) {
65489 // don't re-request tiles which have failed in the past
65490 return _cache[r[3]] !== false;
65494 function load(d3_event, d) {
65495 _cache[d[3]] = true;
65496 select(this).on('error', null).on('load', null).classed('tile-loaded', true);
65500 function error(d3_event, d) {
65501 _cache[d[3]] = false;
65502 select(this).on('error', null).on('load', null).remove();
65506 function imageTransform(d) {
65507 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
65509 var scale = tileSizeAtZoom(d, _zoom);
65510 return 'translate(' + (d[0] * ts - _tileOrigin[0]) + 'px,' + (d[1] * ts - _tileOrigin[1]) + 'px) ' + 'scale(' + scale + ',' + scale + ')';
65513 function tileCenter(d) {
65514 var ts = _tileSize * Math.pow(2, _zoom - d[2]);
65516 return [d[0] * ts - _tileOrigin[0] + ts / 2, d[1] * ts - _tileOrigin[1] + ts / 2];
65519 function debugTransform(d) {
65520 var coord = tileCenter(d);
65521 return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
65522 } // Pick a representative tile near the center of the viewport
65523 // (This is useful for sampling the imagery vintage)
65526 var dims = tiler.size();
65527 var mapCenter = [dims[0] / 2, dims[1] / 2];
65528 var minDist = Math.max(dims[0], dims[1]);
65530 requests.forEach(function (d) {
65531 var c = tileCenter(d);
65532 var dist = geoVecLength(c, mapCenter);
65534 if (dist < minDist) {
65539 var image = selection.selectAll('img').data(requests, function (d) {
65542 image.exit().style(transformProp, imageTransform).classed('tile-removing', true).classed('tile-center', false).each(function () {
65543 var tile = select(this);
65544 window.setTimeout(function () {
65545 if (tile.classed('tile-removing')) {
65550 image.enter().append('img').attr('class', 'tile').attr('draggable', 'false').style('width', _tileSize + 'px').style('height', _tileSize + 'px').attr('src', function (d) {
65552 }).on('error', error).on('load', load).merge(image).style(transformProp, imageTransform).classed('tile-debug', showDebug).classed('tile-removing', false).classed('tile-center', function (d) {
65553 return d === nearCenter;
65555 var debug = selection.selectAll('.tile-label-debug').data(showDebug ? requests : [], function (d) {
65558 debug.exit().remove();
65561 var debugEnter = debug.enter().append('div').attr('class', 'tile-label-debug');
65562 debugEnter.append('div').attr('class', 'tile-label-debug-coord');
65563 debugEnter.append('div').attr('class', 'tile-label-debug-vintage');
65564 debug = debug.merge(debugEnter);
65565 debug.style(transformProp, debugTransform);
65566 debug.selectAll('.tile-label-debug-coord').html(function (d) {
65567 return d[2] + ' / ' + d[0] + ' / ' + d[1];
65569 debug.selectAll('.tile-label-debug-vintage').each(function (d) {
65570 var span = select(this);
65571 var center = context.projection.invert(tileCenter(d));
65573 _source.getMetadata(center, d, function (err, result) {
65574 span.html(result && result.vintage && result.vintage.range || _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown'));
65580 background.projection = function (val) {
65581 if (!arguments.length) return _projection;
65586 background.dimensions = function (val) {
65587 if (!arguments.length) return tiler.size();
65592 background.source = function (val) {
65593 if (!arguments.length) return _source;
65595 _tileSize = _source.tileSize;
65597 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
65604 var _imageryIndex = null;
65605 function rendererBackground(context) {
65606 var dispatch$1 = dispatch('change');
65607 var detected = utilDetect();
65608 var baseLayer = rendererTileLayer(context).projection(context.projection);
65609 var _isValid = true;
65610 var _overlayLayers = [];
65611 var _brightness = 1;
65613 var _saturation = 1;
65614 var _sharpness = 1;
65616 function ensureImageryIndex() {
65617 return _mainFileFetcher.get('imagery').then(function (sources) {
65618 if (_imageryIndex) return _imageryIndex;
65622 }; // use which-polygon to support efficient index and querying for imagery
65624 var features = sources.map(function (source) {
65625 if (!source.polygon) return null; // workaround for editor-layer-index weirdness..
65626 // Add an extra array nest to each element in `source.polygon`
65627 // so the rings are not treated as a bunch of holes:
65628 // what we have: [ [[outer],[hole],[hole]] ]
65629 // what we want: [ [[outer]],[[outer]],[[outer]] ]
65631 var rings = source.polygon.map(function (ring) {
65640 type: 'MultiPolygon',
65644 _imageryIndex.features[source.id] = feature;
65646 }).filter(Boolean);
65647 _imageryIndex.query = whichPolygon_1({
65648 type: 'FeatureCollection',
65650 }); // Instantiate `rendererBackgroundSource` objects for each source
65652 _imageryIndex.backgrounds = sources.map(function (source) {
65653 if (source.type === 'bing') {
65654 return rendererBackgroundSource.Bing(source, dispatch$1);
65655 } else if (/^EsriWorldImagery/.test(source.id)) {
65656 return rendererBackgroundSource.Esri(source);
65658 return rendererBackgroundSource(source);
65662 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None()); // Add 'Custom'
65665 var template = corePreferences('background-custom-template') || '';
65666 var custom = rendererBackgroundSource.Custom(template);
65668 _imageryIndex.backgrounds.unshift(custom);
65670 return _imageryIndex;
65674 function background(selection) {
65675 var currSource = baseLayer.source(); // If we are displaying an Esri basemap at high zoom,
65676 // check its tilemap to see how high the zoom can go
65678 if (context.map().zoom() > 18) {
65679 if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
65680 var center = context.map().center();
65681 currSource.fetchTilemap(center);
65683 } // Is the imagery valid here? - #4827
65686 var sources = background.sources(context.map().extent());
65687 var wasValid = _isValid;
65688 _isValid = !!sources.filter(function (d) {
65689 return d === currSource;
65692 if (wasValid !== _isValid) {
65693 // change in valid status
65694 background.updateImagery();
65697 var baseFilter = '';
65699 if (detected.cssfilters) {
65700 if (_brightness !== 1) {
65701 baseFilter += " brightness(".concat(_brightness, ")");
65704 if (_contrast !== 1) {
65705 baseFilter += " contrast(".concat(_contrast, ")");
65708 if (_saturation !== 1) {
65709 baseFilter += " saturate(".concat(_saturation, ")");
65712 if (_sharpness < 1) {
65714 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
65715 baseFilter += " blur(".concat(blur, "px)");
65719 var base = selection.selectAll('.layer-background').data([0]);
65720 base = base.enter().insert('div', '.layer-data').attr('class', 'layer layer-background').merge(base);
65722 if (detected.cssfilters) {
65723 base.style('filter', baseFilter || null);
65725 base.style('opacity', _brightness);
65728 var imagery = base.selectAll('.layer-imagery').data([0]);
65729 imagery.enter().append('div').attr('class', 'layer layer-imagery').merge(imagery).call(baseLayer);
65730 var maskFilter = '';
65731 var mixBlendMode = '';
65733 if (detected.cssfilters && _sharpness > 1) {
65734 // apply unsharp mask
65735 mixBlendMode = 'overlay';
65736 maskFilter = 'saturate(0) blur(3px) invert(1)';
65737 var contrast = _sharpness - 1;
65738 maskFilter += " contrast(".concat(contrast, ")");
65739 var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
65740 maskFilter += " brightness(".concat(brightness, ")");
65743 var mask = base.selectAll('.layer-unsharp-mask').data(detected.cssfilters && _sharpness > 1 ? [0] : []);
65744 mask.exit().remove();
65745 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);
65746 var overlays = selection.selectAll('.layer-overlay').data(_overlayLayers, function (d) {
65747 return d.source().name();
65749 overlays.exit().remove();
65750 overlays.enter().insert('div', '.layer-data').attr('class', 'layer layer-overlay').merge(overlays).each(function (layer, i, nodes) {
65751 return select(nodes[i]).call(layer);
65755 background.updateImagery = function () {
65756 var currSource = baseLayer.source();
65757 if (context.inIntro() || !currSource) return;
65759 var o = _overlayLayers.filter(function (d) {
65760 return !d.source().isLocatorOverlay() && !d.source().isHidden();
65761 }).map(function (d) {
65762 return d.source().id;
65765 var meters = geoOffsetToMeters(currSource.offset());
65766 var EPSILON = 0.01;
65767 var x = +meters[0].toFixed(2);
65768 var y = +meters[1].toFixed(2);
65769 var hash = utilStringQs(window.location.hash);
65770 var id = currSource.id;
65772 if (id === 'custom') {
65773 id = "custom:".concat(currSource.template());
65777 hash.background = id;
65779 delete hash.background;
65785 delete hash.overlays;
65788 if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
65789 hash.offset = "".concat(x, ",").concat(y);
65791 delete hash.offset;
65794 if (!window.mocha) {
65795 window.location.replace('#' + utilQsString(hash, true));
65798 var imageryUsed = [];
65799 var photoOverlaysUsed = [];
65800 var currUsed = currSource.imageryUsed();
65802 if (currUsed && _isValid) {
65803 imageryUsed.push(currUsed);
65806 _overlayLayers.filter(function (d) {
65807 return !d.source().isLocatorOverlay() && !d.source().isHidden();
65808 }).forEach(function (d) {
65809 return imageryUsed.push(d.source().imageryUsed());
65812 var dataLayer = context.layers().layer('data');
65814 if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
65815 imageryUsed.push(dataLayer.getSrc());
65818 var photoOverlayLayers = {
65819 streetside: 'Bing Streetside',
65820 mapillary: 'Mapillary Images',
65821 'mapillary-map-features': 'Mapillary Map Features',
65822 'mapillary-signs': 'Mapillary Signs',
65823 openstreetcam: 'OpenStreetCam Images'
65826 for (var layerID in photoOverlayLayers) {
65827 var layer = context.layers().layer(layerID);
65829 if (layer && layer.enabled()) {
65830 photoOverlaysUsed.push(layerID);
65831 imageryUsed.push(photoOverlayLayers[layerID]);
65835 context.history().imageryUsed(imageryUsed);
65836 context.history().photoOverlaysUsed(photoOverlaysUsed);
65839 var _checkedBlocklists;
65841 background.sources = function (extent, zoom, includeCurrent) {
65842 if (!_imageryIndex) return []; // called before init()?
65845 (_imageryIndex.query.bbox(extent.rectangle(), true) || []).forEach(function (d) {
65846 return visible[d.id] = true;
65848 var currSource = baseLayer.source();
65849 var osm = context.connection();
65850 var blocklists = osm && osm.imageryBlocklists();
65852 if (blocklists && blocklists !== _checkedBlocklists) {
65853 _imageryIndex.backgrounds.forEach(function (source) {
65854 source.isBlocked = blocklists.some(function (blocklist) {
65855 return blocklist.test(source.template());
65859 _checkedBlocklists = blocklists;
65862 return _imageryIndex.backgrounds.filter(function (source) {
65863 if (includeCurrent && currSource === source) return true; // optionally always include the current imagery
65865 if (source.isBlocked) return false; // even bundled sources may be blocked - #7905
65867 if (!source.polygon) return true; // always include imagery with worldwide coverage
65869 if (zoom && zoom < 6) return false; // optionally exclude local imagery at low zooms
65871 return visible[source.id]; // include imagery visible in given extent
65875 background.dimensions = function (val) {
65877 baseLayer.dimensions(val);
65879 _overlayLayers.forEach(function (layer) {
65880 return layer.dimensions(val);
65884 background.baseLayerSource = function (d) {
65885 if (!arguments.length) return baseLayer.source(); // test source against OSM imagery blocklists..
65887 var osm = context.connection();
65888 if (!osm) return background;
65889 var blocklists = osm.imageryBlocklists();
65890 var template = d.template();
65895 for (var i = 0; i < blocklists.length; i++) {
65896 regex = blocklists[i];
65897 fail = regex.test(template);
65900 } // ensure at least one test was run.
65904 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
65905 fail = regex.test(template);
65908 baseLayer.source(!fail ? d : background.findSource('none'));
65909 dispatch$1.call('change');
65910 background.updateImagery();
65914 background.findSource = function (id) {
65915 if (!id || !_imageryIndex) return null; // called before init()?
65917 return _imageryIndex.backgrounds.find(function (d) {
65918 return d.id && d.id === id;
65922 background.bing = function () {
65923 background.baseLayerSource(background.findSource('Bing'));
65926 background.showsLayer = function (d) {
65927 var currSource = baseLayer.source();
65928 if (!d || !currSource) return false;
65929 return d.id === currSource.id || _overlayLayers.some(function (layer) {
65930 return d.id === layer.source().id;
65934 background.overlayLayerSources = function () {
65935 return _overlayLayers.map(function (layer) {
65936 return layer.source();
65940 background.toggleOverlayLayer = function (d) {
65943 for (var i = 0; i < _overlayLayers.length; i++) {
65944 layer = _overlayLayers[i];
65946 if (layer.source() === d) {
65947 _overlayLayers.splice(i, 1);
65949 dispatch$1.call('change');
65950 background.updateImagery();
65955 layer = rendererTileLayer(context).source(d).projection(context.projection).dimensions(baseLayer.dimensions());
65957 _overlayLayers.push(layer);
65959 dispatch$1.call('change');
65960 background.updateImagery();
65963 background.nudge = function (d, zoom) {
65964 var currSource = baseLayer.source();
65967 currSource.nudge(d, zoom);
65968 dispatch$1.call('change');
65969 background.updateImagery();
65975 background.offset = function (d) {
65976 var currSource = baseLayer.source();
65978 if (!arguments.length) {
65979 return currSource && currSource.offset() || [0, 0];
65983 currSource.offset(d);
65984 dispatch$1.call('change');
65985 background.updateImagery();
65991 background.brightness = function (d) {
65992 if (!arguments.length) return _brightness;
65994 if (context.mode()) dispatch$1.call('change');
65998 background.contrast = function (d) {
65999 if (!arguments.length) return _contrast;
66001 if (context.mode()) dispatch$1.call('change');
66005 background.saturation = function (d) {
66006 if (!arguments.length) return _saturation;
66008 if (context.mode()) dispatch$1.call('change');
66012 background.sharpness = function (d) {
66013 if (!arguments.length) return _sharpness;
66015 if (context.mode()) dispatch$1.call('change');
66021 background.ensureLoaded = function () {
66022 if (_loadPromise) return _loadPromise;
66024 function parseMapParams(qmap) {
66025 if (!qmap) return false;
66026 var params = qmap.split('/').map(Number);
66027 if (params.length < 3 || params.some(isNaN)) return false;
66028 return geoExtent([params[2], params[1]]); // lon,lat
66031 var hash = utilStringQs(window.location.hash);
66032 var requested = hash.background || hash.layer;
66033 var extent = parseMapParams(hash.map);
66034 return _loadPromise = ensureImageryIndex().then(function (imageryIndex) {
66035 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
66038 if (!requested && extent) {
66039 best = background.sources(extent).find(function (s) {
66042 } // Decide which background layer to display
66045 if (requested && requested.indexOf('custom:') === 0) {
66046 var template = requested.replace(/^custom:/, '');
66047 var custom = background.findSource('custom');
66048 background.baseLayerSource(custom.template(template));
66049 corePreferences('background-custom-template', template);
66051 background.baseLayerSource(background.findSource(requested) || best || background.findSource(corePreferences('background-last-used')) || background.findSource('Bing') || first || background.findSource('none'));
66054 var locator = imageryIndex.backgrounds.find(function (d) {
66055 return d.overlay && d["default"];
66059 background.toggleOverlayLayer(locator);
66062 var overlays = (hash.overlays || '').split(',');
66063 overlays.forEach(function (overlay) {
66064 overlay = background.findSource(overlay);
66067 background.toggleOverlayLayer(overlay);
66072 var gpx = context.layers().layer('data');
66075 gpx.url(hash.gpx, '.gpx');
66080 var offset = hash.offset.replace(/;/g, ',').split(',').map(function (n) {
66081 return !isNaN(n) && n;
66084 if (offset.length === 2) {
66085 background.offset(geoMetersToOffset(offset));
66088 })["catch"](function () {
66093 return utilRebind(background, dispatch$1, 'on');
66096 function rendererFeatures(context) {
66097 var dispatch$1 = dispatch('change', 'redraw');
66098 var features = utilRebind({}, dispatch$1, 'on');
66100 var _deferred = new Set();
66102 var traffic_roads = {
66104 'motorway_link': true,
66106 'trunk_link': true,
66108 'primary_link': true,
66110 'secondary_link': true,
66112 'tertiary_link': true,
66113 'residential': true,
66114 'unclassified': true,
66115 'living_street': true
66117 var service_roads = {
66130 var past_futures = {
66132 'construction': true,
66134 'dismantled': true,
66137 'demolished': true,
66138 'obliterated': true
66140 var _cullFactor = 1;
66146 var _forceVisible = {};
66148 function update() {
66149 if (!window.mocha) {
66150 var hash = utilStringQs(window.location.hash);
66151 var disabled = features.disabled();
66153 if (disabled.length) {
66154 hash.disable_features = disabled.join(',');
66156 delete hash.disable_features;
66159 window.location.replace('#' + utilQsString(hash, true));
66160 corePreferences('disabled-features', disabled.join(','));
66163 _hidden = features.hidden();
66164 dispatch$1.call('change');
66165 dispatch$1.call('redraw');
66168 function defineRule(k, filter, max) {
66169 var isEnabled = true;
66175 enabled: isEnabled,
66176 // whether the user wants it enabled..
66178 currentMax: max || Infinity,
66179 defaultMax: max || Infinity,
66180 enable: function enable() {
66181 this.enabled = true;
66182 this.currentMax = this.defaultMax;
66184 disable: function disable() {
66185 this.enabled = false;
66186 this.currentMax = 0;
66188 hidden: function hidden() {
66189 return this.count === 0 && !this.enabled || this.count > this.currentMax * _cullFactor;
66191 autoHidden: function autoHidden() {
66192 return this.hidden() && this.currentMax > 0;
66197 defineRule('points', function isPoint(tags, geometry) {
66198 return geometry === 'point';
66200 defineRule('traffic_roads', function isTrafficRoad(tags) {
66201 return traffic_roads[tags.highway];
66203 defineRule('service_roads', function isServiceRoad(tags) {
66204 return service_roads[tags.highway];
66206 defineRule('paths', function isPath(tags) {
66207 return paths[tags.highway];
66209 defineRule('buildings', function isBuilding(tags) {
66210 return !!tags.building && tags.building !== 'no' || tags.parking === 'multi-storey' || tags.parking === 'sheds' || tags.parking === 'carports' || tags.parking === 'garage_boxes';
66212 defineRule('building_parts', function isBuildingPart(tags) {
66213 return tags['building:part'];
66215 defineRule('indoor', function isIndoor(tags) {
66216 return tags.indoor;
66218 defineRule('landuse', function isLanduse(tags, geometry) {
66219 return geometry === 'area' && !_rules.buildings.filter(tags) && !_rules.building_parts.filter(tags) && !_rules.indoor.filter(tags) && !_rules.water.filter(tags) && !_rules.pistes.filter(tags);
66221 defineRule('boundaries', function isBoundary(tags) {
66222 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);
66224 defineRule('water', function isWater(tags) {
66225 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';
66227 defineRule('rail', function isRail(tags) {
66228 return (!!tags.railway || tags.landuse === 'railway') && !(traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]);
66230 defineRule('pistes', function isPiste(tags) {
66231 return tags['piste:type'];
66233 defineRule('aerialways', function isPiste(tags) {
66234 return tags.aerialway && tags.aerialway !== 'yes' && tags.aerialway !== 'station';
66236 defineRule('power', function isPower(tags) {
66237 return !!tags.power;
66238 }); // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
66240 defineRule('past_future', function isPastFuture(tags) {
66241 if (traffic_roads[tags.highway] || service_roads[tags.highway] || paths[tags.highway]) {
66245 var strings = Object.keys(tags);
66247 for (var i = 0; i < strings.length; i++) {
66248 var s = strings[i];
66250 if (past_futures[s] || past_futures[tags[s]]) {
66256 }); // Lines or areas that don't match another feature filter.
66257 // IMPORTANT: The 'others' feature must be the last one defined,
66258 // so that code in getMatches can skip this test if `hasMatch = true`
66260 defineRule('others', function isOther(tags, geometry) {
66261 return geometry === 'line' || geometry === 'area';
66264 features.features = function () {
66268 features.keys = function () {
66272 features.enabled = function (k) {
66273 if (!arguments.length) {
66274 return _keys.filter(function (k) {
66275 return _rules[k].enabled;
66279 return _rules[k] && _rules[k].enabled;
66282 features.disabled = function (k) {
66283 if (!arguments.length) {
66284 return _keys.filter(function (k) {
66285 return !_rules[k].enabled;
66289 return _rules[k] && !_rules[k].enabled;
66292 features.hidden = function (k) {
66293 if (!arguments.length) {
66294 return _keys.filter(function (k) {
66295 return _rules[k].hidden();
66299 return _rules[k] && _rules[k].hidden();
66302 features.autoHidden = function (k) {
66303 if (!arguments.length) {
66304 return _keys.filter(function (k) {
66305 return _rules[k].autoHidden();
66309 return _rules[k] && _rules[k].autoHidden();
66312 features.enable = function (k) {
66313 if (_rules[k] && !_rules[k].enabled) {
66314 _rules[k].enable();
66320 features.enableAll = function () {
66321 var didEnable = false;
66323 for (var k in _rules) {
66324 if (!_rules[k].enabled) {
66327 _rules[k].enable();
66331 if (didEnable) update();
66334 features.disable = function (k) {
66335 if (_rules[k] && _rules[k].enabled) {
66336 _rules[k].disable();
66342 features.disableAll = function () {
66343 var didDisable = false;
66345 for (var k in _rules) {
66346 if (_rules[k].enabled) {
66349 _rules[k].disable();
66353 if (didDisable) update();
66356 features.toggle = function (k) {
66359 return f.enabled ? f.disable() : f.enable();
66366 features.resetStats = function () {
66367 for (var i = 0; i < _keys.length; i++) {
66368 _rules[_keys[i]].count = 0;
66371 dispatch$1.call('change');
66374 features.gatherStats = function (d, resolver, dimensions) {
66375 var needsRedraw = false;
66376 var types = utilArrayGroupBy(d, 'type');
66377 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
66378 var currHidden, geometry, matches, i, j;
66380 for (i = 0; i < _keys.length; i++) {
66381 _rules[_keys[i]].count = 0;
66382 } // adjust the threshold for point/building culling based on viewport size..
66383 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
66386 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
66388 for (i = 0; i < entities.length; i++) {
66389 geometry = entities[i].geometry(resolver);
66390 matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
66392 for (j = 0; j < matches.length; j++) {
66393 _rules[matches[j]].count++;
66397 currHidden = features.hidden();
66399 if (currHidden !== _hidden) {
66400 _hidden = currHidden;
66401 needsRedraw = true;
66402 dispatch$1.call('change');
66405 return needsRedraw;
66408 features.stats = function () {
66409 for (var i = 0; i < _keys.length; i++) {
66410 _stats[_keys[i]] = _rules[_keys[i]].count;
66416 features.clear = function (d) {
66417 for (var i = 0; i < d.length; i++) {
66418 features.clearEntity(d[i]);
66422 features.clearEntity = function (entity) {
66423 delete _cache[osmEntity.key(entity)];
66426 features.reset = function () {
66427 Array.from(_deferred).forEach(function (handle) {
66428 window.cancelIdleCallback(handle);
66430 _deferred["delete"](handle);
66433 }; // only certain relations are worth checking
66436 function relationShouldBeChecked(relation) {
66437 // multipolygon features have `area` geometry and aren't checked here
66438 return relation.tags.type === 'boundary';
66441 features.getMatches = function (entity, resolver, geometry) {
66442 if (geometry === 'vertex' || geometry === 'relation' && !relationShouldBeChecked(entity)) return {};
66443 var ent = osmEntity.key(entity);
66445 if (!_cache[ent]) {
66449 if (!_cache[ent].matches) {
66451 var hasMatch = false;
66453 for (var i = 0; i < _keys.length; i++) {
66454 if (_keys[i] === 'others') {
66455 if (hasMatch) continue; // If an entity...
66456 // 1. is a way that hasn't matched other 'interesting' feature rules,
66458 if (entity.type === 'way') {
66459 var parents = features.getParents(entity, resolver, geometry); // 2a. belongs only to a single multipolygon relation
66461 if (parents.length === 1 && parents[0].isMultipolygon() || // 2b. or belongs only to boundary relations
66462 parents.length > 0 && parents.every(function (parent) {
66463 return parent.tags.type === 'boundary';
66465 // ...then match whatever feature rules the parent relation has matched.
66466 // see #2548, #2887
66469 // For this to work, getMatches must be called on relations before ways.
66471 var pkey = osmEntity.key(parents[0]);
66473 if (_cache[pkey] && _cache[pkey].matches) {
66474 matches = Object.assign({}, _cache[pkey].matches); // shallow copy
66482 if (_rules[_keys[i]].filter(entity.tags, geometry)) {
66483 matches[_keys[i]] = hasMatch = true;
66487 _cache[ent].matches = matches;
66490 return _cache[ent].matches;
66493 features.getParents = function (entity, resolver, geometry) {
66494 if (geometry === 'point') return [];
66495 var ent = osmEntity.key(entity);
66497 if (!_cache[ent]) {
66501 if (!_cache[ent].parents) {
66504 if (geometry === 'vertex') {
66505 parents = resolver.parentWays(entity);
66507 // 'line', 'area', 'relation'
66508 parents = resolver.parentRelations(entity);
66511 _cache[ent].parents = parents;
66514 return _cache[ent].parents;
66517 features.isHiddenPreset = function (preset, geometry) {
66518 if (!_hidden.length) return false;
66519 if (!preset.tags) return false;
66520 var test = preset.setTags({}, geometry);
66522 for (var key in _rules) {
66523 if (_rules[key].filter(test, geometry)) {
66524 if (_hidden.indexOf(key) !== -1) {
66535 features.isHiddenFeature = function (entity, resolver, geometry) {
66536 if (!_hidden.length) return false;
66537 if (!entity.version) return false;
66538 if (_forceVisible[entity.id]) return false;
66539 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
66540 return matches.length && matches.every(function (k) {
66541 return features.hidden(k);
66545 features.isHiddenChild = function (entity, resolver, geometry) {
66546 if (!_hidden.length) return false;
66547 if (!entity.version || geometry === 'point') return false;
66548 if (_forceVisible[entity.id]) return false;
66549 var parents = features.getParents(entity, resolver, geometry);
66550 if (!parents.length) return false;
66552 for (var i = 0; i < parents.length; i++) {
66553 if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
66561 features.hasHiddenConnections = function (entity, resolver) {
66562 if (!_hidden.length) return false;
66563 var childNodes, connections;
66565 if (entity.type === 'midpoint') {
66566 childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
66569 childNodes = entity.nodes ? resolver.childNodes(entity) : [];
66570 connections = features.getParents(entity, resolver, entity.geometry(resolver));
66571 } // gather ways connected to child nodes..
66574 connections = childNodes.reduce(function (result, e) {
66575 return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
66577 return connections.some(function (e) {
66578 return features.isHidden(e, resolver, e.geometry(resolver));
66582 features.isHidden = function (entity, resolver, geometry) {
66583 if (!_hidden.length) return false;
66584 if (!entity.version) return false;
66585 var fn = geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature;
66586 return fn(entity, resolver, geometry);
66589 features.filter = function (d, resolver) {
66590 if (!_hidden.length) return d;
66593 for (var i = 0; i < d.length; i++) {
66596 if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
66597 result.push(entity);
66604 features.forceVisible = function (entityIDs) {
66605 if (!arguments.length) return Object.keys(_forceVisible);
66606 _forceVisible = {};
66608 for (var i = 0; i < entityIDs.length; i++) {
66609 _forceVisible[entityIDs[i]] = true;
66610 var entity = context.hasEntity(entityIDs[i]);
66612 if (entity && entity.type === 'relation') {
66613 // also show relation members (one level deep)
66614 for (var j in entity.members) {
66615 _forceVisible[entity.members[j].id] = true;
66623 features.init = function () {
66624 var storage = corePreferences('disabled-features');
66627 var storageDisabled = storage.replace(/;/g, ',').split(',');
66628 storageDisabled.forEach(features.disable);
66631 var hash = utilStringQs(window.location.hash);
66633 if (hash.disable_features) {
66634 var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
66635 hashDisabled.forEach(features.disable);
66637 }; // warm up the feature matching cache upon merging fetched data
66640 context.history().on('merge.features', function (newEntities) {
66641 if (!newEntities) return;
66642 var handle = window.requestIdleCallback(function () {
66643 var graph = context.graph();
66644 var types = utilArrayGroupBy(newEntities, 'type'); // ensure that getMatches is called on relations before ways
66646 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
66648 for (var i = 0; i < entities.length; i++) {
66649 var geometry = entities[i].geometry(graph);
66650 features.getMatches(entities[i], graph, geometry);
66654 _deferred.add(handle);
66660 // - the activeID - nope
66661 // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
66662 // - 2 away from the activeID - nope (would create a self intersecting segment)
66663 // - all others on a linear way - yes
66664 // - all others on a closed way - nope (would create a self intersecting polygon)
66667 // 0 = active vertex - no touch/connect
66668 // 1 = passive vertex - yes touch/connect
66669 // 2 = adjacent vertex - yes but pay attention segmenting a line here
66672 function svgPassiveVertex(node, graph, activeID) {
66673 if (!activeID) return 1;
66674 if (activeID === node.id) return 0;
66675 var parents = graph.parentWays(node);
66676 var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
66678 for (i = 0; i < parents.length; i++) {
66679 nodes = parents[i].nodes;
66680 isClosed = parents[i].isClosed();
66682 for (j = 0; j < nodes.length; j++) {
66683 // find this vertex, look nearby
66684 if (nodes[j] === node.id) {
66691 // wraparound if needed
66692 max = nodes.length - 1;
66693 if (ix1 < 0) ix1 = max + ix1;
66694 if (ix2 < 0) ix2 = max + ix2;
66695 if (ix3 > max) ix3 = ix3 - max;
66696 if (ix4 > max) ix4 = ix4 - max;
66699 if (nodes[ix1] === activeID) return 0; // no - prevent self intersect
66700 else if (nodes[ix2] === activeID) return 2; // ok - adjacent
66701 else if (nodes[ix3] === activeID) return 2; // ok - adjacent
66702 else if (nodes[ix4] === activeID) return 0; // no - prevent self intersect
66703 else if (isClosed && nodes.indexOf(activeID) !== -1) return 0; // no - prevent self intersect
66710 function svgMarkerSegments(projection, graph, dt, shouldReverse, bothDirections) {
66711 return function (entity) {
66715 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
66716 var coordinates = graph.childNodes(entity).map(function (n) {
66721 if (shouldReverse(entity)) {
66722 coordinates.reverse();
66726 type: 'LineString',
66727 coordinates: coordinates
66728 }, projection.stream(clip({
66729 lineStart: function lineStart() {},
66730 lineEnd: function lineEnd() {
66733 point: function point(x, y) {
66737 var span = geoVecLength(a, b) - offset;
66740 var heading = geoVecAngle(a, b);
66741 var dx = dt * Math.cos(heading);
66742 var dy = dt * Math.sin(heading);
66743 var p = [a[0] + offset * Math.cos(heading), a[1] + offset * Math.sin(heading)]; // gather coordinates
66745 var coord = [a, p];
66747 for (span -= dt; span >= 0; span -= dt) {
66748 p = geoVecAdd(p, [dx, dy]);
66752 coord.push(b); // generate svg paths
66757 for (j = 0; j < coord.length; j++) {
66758 segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
66767 if (bothDirections(entity)) {
66770 for (j = coord.length - 1; j >= 0; j--) {
66771 segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
66791 function svgPath(projection, graph, isArea) {
66792 // Explanation of magic numbers:
66793 // "padding" here allows space for strokes to extend beyond the viewport,
66794 // so that the stroke isn't drawn along the edge of the viewport when
66795 // the shape is clipped.
66797 // When drawing lines, pad viewport by 5px.
66798 // When drawing areas, pad viewport by 65px in each direction to allow
66799 // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
66801 var padding = isArea ? 65 : 5;
66802 var viewport = projection.clipExtent();
66803 var paddedExtent = [[viewport[0][0] - padding, viewport[0][1] - padding], [viewport[1][0] + padding, viewport[1][1] + padding]];
66804 var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
66805 var project = projection.stream;
66806 var path = d3_geoPath().projection({
66807 stream: function stream(output) {
66808 return project(clip(output));
66812 var svgpath = function svgpath(entity) {
66813 if (entity.id in cache) {
66814 return cache[entity.id];
66816 return cache[entity.id] = path(entity.asGeoJSON(graph));
66820 svgpath.geojson = function (d) {
66821 if (d.__featurehash__ !== undefined) {
66822 if (d.__featurehash__ in cache) {
66823 return cache[d.__featurehash__];
66825 return cache[d.__featurehash__] = path(d);
66834 function svgPointTransform(projection) {
66835 var svgpoint = function svgpoint(entity) {
66836 // http://jsperf.com/short-array-join
66837 var pt = projection(entity.loc);
66838 return 'translate(' + pt[0] + ',' + pt[1] + ')';
66841 svgpoint.geojson = function (d) {
66842 return svgpoint(d.properties.entity);
66847 function svgRelationMemberTags(graph) {
66848 return function (entity) {
66849 var tags = entity.tags;
66850 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
66851 graph.parentRelations(entity).forEach(function (relation) {
66852 var type = relation.tags.type;
66854 if (type === 'multipolygon' && shouldCopyMultipolygonTags || type === 'boundary') {
66855 tags = Object.assign({}, relation.tags, tags);
66861 function svgSegmentWay(way, graph, activeID) {
66862 // When there is no activeID, we can memoize this expensive computation
66863 if (activeID === undefined) {
66864 return graph["transient"](way, 'waySegments', getWaySegments);
66866 return getWaySegments();
66869 function getWaySegments() {
66870 var isActiveWay = way.nodes.indexOf(activeID) !== -1;
66879 for (var i = 0; i < way.nodes.length; i++) {
66880 node = graph.entity(way.nodes[i]);
66881 type = svgPassiveVertex(node, graph, activeID);
66887 if (start.type !== undefined) {
66888 if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {
66889 // one adjacent vertex
66890 pushActive(start, end, i);
66891 } else if (start.type === 0 && end.type === 0) {
66892 // both active vertices
66893 pushActive(start, end, i);
66895 pushPassive(start, end, i);
66904 function pushActive(start, end, index) {
66905 features.active.push({
66907 id: way.id + '-' + index + '-nope',
66912 nodes: [start.node, end.node],
66916 type: 'LineString',
66917 coordinates: [start.node.loc, end.node.loc]
66922 function pushPassive(start, end, index) {
66923 features.passive.push({
66925 id: way.id + '-' + index,
66929 nodes: [start.node, end.node],
66933 type: 'LineString',
66934 coordinates: [start.node.loc, end.node.loc]
66941 function svgTagClasses() {
66942 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'];
66943 var statuses = [// nonexistent, might be built
66944 'proposed', 'planned', // under maintentance or between groundbreaking and opening
66945 'construction', // existent but not functional
66946 'disused', // dilapidated to nonexistent
66947 'abandoned', // nonexistent, still may appear in imagery
66948 'dismantled', 'razed', 'demolished', 'obliterated', // existent occasionally, e.g. stormwater drainage basin
66950 var secondaries = ['oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure', 'man_made', 'indoor'];
66952 var _tags = function _tags(entity) {
66953 return entity.tags;
66956 var tagClasses = function tagClasses(selection) {
66957 selection.each(function tagClassesEach(entity) {
66958 var value = this.className;
66960 if (value.baseVal !== undefined) {
66961 value = value.baseVal;
66964 var t = _tags(entity);
66966 var computed = tagClasses.getClassesString(t, value);
66968 if (computed !== value) {
66969 select(this).attr('class', computed);
66974 tagClasses.getClassesString = function (t, value) {
66975 var primary, status;
66976 var i, j, k, v; // in some situations we want to render perimeter strokes a certain way
66978 var overrideGeometry;
66980 if (/\bstroke\b/.test(value)) {
66981 if (!!t.barrier && t.barrier !== 'no') {
66982 overrideGeometry = 'line';
66984 } // preserve base classes (nothing with `tag-`)
66987 var classes = value.trim().split(/\s+/).filter(function (klass) {
66988 return klass.length && !/^tag-/.test(klass);
66989 }).map(function (klass) {
66990 // special overrides for some perimeter strokes
66991 return klass === 'line' || klass === 'area' ? overrideGeometry || klass : klass;
66992 }); // pick at most one primary classification tag..
66994 for (i = 0; i < primaries.length; i++) {
66997 if (!v || v === 'no') continue;
66999 if (k === 'piste:type') {
67000 // avoid a ':' in the class name
67002 } else if (k === 'building:part') {
67003 // avoid a ':' in the class name
67004 k = 'building_part';
67009 if (statuses.indexOf(v) !== -1) {
67010 // e.g. `railway=abandoned`
67012 classes.push('tag-' + k);
67014 classes.push('tag-' + k);
67015 classes.push('tag-' + k + '-' + v);
67022 for (i = 0; i < statuses.length; i++) {
67023 for (j = 0; j < primaries.length; j++) {
67024 k = statuses[i] + ':' + primaries[j]; // e.g. `demolished:building=yes`
67027 if (!v || v === 'no') continue;
67028 status = statuses[i];
67032 } // add at most one status tag, only if relates to primary tag..
67036 for (i = 0; i < statuses.length; i++) {
67039 if (!v || v === 'no') continue;
67042 // e.g. `railway=rail + abandoned=yes`
67044 } else if (primary && primary === v) {
67045 // e.g. `railway=rail + abandoned=railway`
67047 } else if (!primary && primaries.indexOf(v) !== -1) {
67048 // e.g. `abandoned=railway`
67051 classes.push('tag-' + v);
67052 } // else ignore e.g. `highway=path + abandoned=railway`
67060 classes.push('tag-status');
67061 classes.push('tag-status-' + status);
67062 } // add any secondary tags
67065 for (i = 0; i < secondaries.length; i++) {
67066 k = secondaries[i];
67068 if (!v || v === 'no' || k === primary) continue;
67069 classes.push('tag-' + k);
67070 classes.push('tag-' + k + '-' + v);
67071 } // For highways, look for surface tagging..
67074 if (primary === 'highway' && !osmPathHighwayTagValues[t.highway] || primary === 'aeroway') {
67075 var surface = t.highway === 'track' ? 'unpaved' : 'paved';
67080 if (k in osmPavedTags) {
67081 surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
67084 if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
67085 surface = 'semipaved';
67089 classes.push('tag-' + surface);
67090 } // If this is a wikidata-tagged item, add a class for that..
67093 if (t.wikidata || t['brand:wikidata']) {
67094 classes.push('tag-wikidata');
67097 return classes.join(' ').trim();
67100 tagClasses.tags = function (val) {
67101 if (!arguments.length) return _tags;
67109 // Patterns only work in Firefox when set directly on element.
67110 // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
67112 // tag - pattern name
67114 // tag - value - pattern name
67116 // tag - value - rules (optional tag-values, pattern name)
67117 // (matches earlier rules first, so fallback should be last entry)
67119 grave_yard: 'cemetery',
67120 fountain: 'water_standing'
67124 religion: 'christian',
67125 pattern: 'cemetery_christian'
67127 religion: 'buddhist',
67128 pattern: 'cemetery_buddhist'
67130 religion: 'muslim',
67131 pattern: 'cemetery_muslim'
67133 religion: 'jewish',
67134 pattern: 'cemetery_jewish'
67136 pattern: 'cemetery'
67138 construction: 'construction',
67139 farmland: 'farmland',
67140 farmyard: 'farmyard',
67142 leaf_type: 'broadleaved',
67143 pattern: 'forest_broadleaved'
67145 leaf_type: 'needleleaved',
67146 pattern: 'forest_needleleaved'
67148 leaf_type: 'leafless',
67149 pattern: 'forest_leafless'
67152 } // same as 'leaf_type:mixed'
67154 grave_yard: 'cemetery',
67157 pattern: 'golf_green'
67161 landfill: 'landfill',
67163 military: 'construction',
67164 orchard: 'orchard',
67166 vineyard: 'vineyard'
67170 grassland: 'grass',
67177 water: 'reservoir',
67178 pattern: 'water_standing'
67184 pattern: 'wetland_marsh'
67187 pattern: 'wetland_swamp'
67190 pattern: 'wetland_bog'
67192 wetland: 'reedbed',
67193 pattern: 'wetland_reedbed'
67198 leaf_type: 'broadleaved',
67199 pattern: 'forest_broadleaved'
67201 leaf_type: 'needleleaved',
67202 pattern: 'forest_needleleaved'
67204 leaf_type: 'leafless',
67205 pattern: 'forest_leafless'
67208 } // same as 'leaf_type:mixed'
67226 function svgTagPattern(tags) {
67227 // Skip pattern filling if this is a building (buildings don't get patterns applied)
67228 if (tags.building && tags.building !== 'no') {
67232 for (var tag in patterns) {
67233 var entityValue = tags[tag];
67234 if (!entityValue) continue;
67236 if (typeof patterns[tag] === 'string') {
67237 // extra short syntax (just tag) - pattern name
67238 return 'pattern-' + patterns[tag];
67240 var values = patterns[tag];
67242 for (var value in values) {
67243 if (entityValue !== value) continue;
67244 var rules = values[value];
67246 if (typeof rules === 'string') {
67247 // short syntax - pattern name
67248 return 'pattern-' + rules;
67249 } // long syntax - rule array
67252 for (var ruleKey in rules) {
67253 var rule = rules[ruleKey];
67256 for (var criterion in rule) {
67257 if (criterion !== 'pattern') {
67258 // reserved for pattern name
67259 // The only rule is a required tag-value pair
67260 var v = tags[criterion];
67262 if (!v || v !== rule[criterion]) {
67270 return 'pattern-' + rule.pattern;
67280 function svgAreas(projection, context) {
67281 function getPatternStyle(tags) {
67282 var imageID = svgTagPattern(tags);
67285 return 'url("#ideditor-' + imageID + '")';
67291 function drawTargets(selection, graph, entities, filter) {
67292 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
67293 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
67294 var getPath = svgPath(projection).geojson;
67295 var activeID = context.activeID();
67296 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
67302 entities.forEach(function (way) {
67303 var features = svgSegmentWay(way, graph, activeID);
67304 data.targets.push.apply(data.targets, features.passive);
67305 data.nopes.push.apply(data.nopes, features.active);
67306 }); // Targets allow hover and vertex snapping
67308 var targetData = data.targets.filter(getPath);
67309 var targets = selection.selectAll('.area.target-allowed').filter(function (d) {
67310 return filter(d.properties.entity);
67311 }).data(targetData, function key(d) {
67315 targets.exit().remove();
67317 var segmentWasEdited = function segmentWasEdited(d) {
67318 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
67320 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
67324 return d.properties.nodes.some(function (n) {
67325 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
67330 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
67331 return 'way area target target-allowed ' + targetClass + d.id;
67332 }).classed('segment-edited', segmentWasEdited); // NOPE
67334 var nopeData = data.nopes.filter(getPath);
67335 var nopes = selection.selectAll('.area.target-nope').filter(function (d) {
67336 return filter(d.properties.entity);
67337 }).data(nopeData, function key(d) {
67341 nopes.exit().remove(); // enter/update
67343 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
67344 return 'way area target target-nope ' + nopeClass + d.id;
67345 }).classed('segment-edited', segmentWasEdited);
67348 function drawAreas(selection, graph, entities, filter) {
67349 var path = svgPath(projection, graph, true);
67352 var base = context.history().base();
67354 for (var i = 0; i < entities.length; i++) {
67355 var entity = entities[i];
67356 if (entity.geometry(graph) !== 'area') continue;
67357 multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
67359 if (multipolygon) {
67360 areas[multipolygon.id] = {
67361 entity: multipolygon.mergeTags(entity.tags),
67362 area: Math.abs(entity.area(graph))
67364 } else if (!areas[entity.id]) {
67365 areas[entity.id] = {
67367 area: Math.abs(entity.area(graph))
67372 var fills = Object.values(areas).filter(function hasPath(a) {
67373 return path(a.entity);
67375 fills.sort(function areaSort(a, b) {
67376 return b.area - a.area;
67378 fills = fills.map(function (a) {
67381 var strokes = fills.filter(function (area) {
67382 return area.type === 'way';
67390 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm').filter(filter).data(data.clip, osmEntity.key);
67391 clipPaths.exit().remove();
67392 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-osm').attr('id', function (entity) {
67393 return 'ideditor-' + entity.id + '-clippath';
67395 clipPathsEnter.append('path');
67396 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', path);
67397 var drawLayer = selection.selectAll('.layer-osm.areas');
67398 var touchLayer = selection.selectAll('.layer-touch.areas'); // Draw areas..
67400 var areagroup = drawLayer.selectAll('g.areagroup').data(['fill', 'shadow', 'stroke']);
67401 areagroup = areagroup.enter().append('g').attr('class', function (d) {
67402 return 'areagroup area-' + d;
67403 }).merge(areagroup);
67404 var paths = areagroup.selectAll('path').filter(filter).data(function (layer) {
67405 return data[layer];
67407 paths.exit().remove();
67408 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
67409 var bisect = d3_bisector(function (node) {
67410 return -node.__data__.area(graph);
67413 function sortedByArea(entity) {
67414 if (this._parent.__data__ === 'fill') {
67415 return fillpaths[bisect(fillpaths, -entity.area(graph))];
67419 paths = paths.enter().insert('path', sortedByArea).merge(paths).each(function (entity) {
67420 var layer = this.parentNode.__data__;
67421 this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
67423 if (layer === 'fill') {
67424 this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
67425 this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
67427 }).classed('added', function (d) {
67428 return !base.entities[d.id];
67429 }).classed('geometry-edited', function (d) {
67430 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
67431 }).classed('retagged', function (d) {
67432 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
67433 }).call(svgTagClasses()).attr('d', path); // Draw touch targets..
67435 touchLayer.call(drawTargets, graph, data.stroke, filter);
67441 //[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]
67442 //[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
67443 //[5] Name ::= NameStartChar (NameChar)*
67444 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
67446 var nameChar = new RegExp("[\\-\\.0-9" + nameStartChar.source.slice(1, -1) + "\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
67447 var tagNamePattern = new RegExp('^' + nameStartChar.source + nameChar.source + '*(?:\:' + nameStartChar.source + nameChar.source + '*)?$'); //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
67448 //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(',')
67449 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
67450 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
67452 var S_TAG = 0; //tag name offerring
67454 var S_ATTR = 1; //attr name offerring
67456 var S_ATTR_SPACE = 2; //attr name end and space offer
67458 var S_EQ = 3; //=space?
67460 var S_ATTR_NOQUOT_VALUE = 4; //attr value(no quot value only)
67462 var S_ATTR_END = 5; //attr value end and no space(quot end)
67464 var S_TAG_SPACE = 6; //(attr value end || tag end ) && (space offer)
67466 var S_TAG_CLOSE = 7; //closed el<el />
67468 function XMLReader() {}
67470 XMLReader.prototype = {
67471 parse: function parse(source, defaultNSMap, entityMap) {
67472 var domBuilder = this.domBuilder;
67473 domBuilder.startDocument();
67475 _copy(defaultNSMap, defaultNSMap = {});
67477 _parse(source, defaultNSMap, entityMap, domBuilder, this.errorHandler);
67479 domBuilder.endDocument();
67483 function _parse(source, defaultNSMapCopy, entityMap, domBuilder, errorHandler) {
67484 function fixedFromCharCode(code) {
67485 // String.prototype.fromCharCode does not supports
67486 // > 2 bytes unicode chars directly
67487 if (code > 0xffff) {
67489 var surrogate1 = 0xd800 + (code >> 10),
67490 surrogate2 = 0xdc00 + (code & 0x3ff);
67491 return String.fromCharCode(surrogate1, surrogate2);
67493 return String.fromCharCode(code);
67497 function entityReplacer(a) {
67498 var k = a.slice(1, -1);
67500 if (k in entityMap) {
67501 return entityMap[k];
67502 } else if (k.charAt(0) === '#') {
67503 return fixedFromCharCode(parseInt(k.substr(1).replace('x', '0x')));
67505 errorHandler.error('entity not found:' + a);
67510 function appendText(end) {
67513 var xt = source.substring(start, end).replace(/&#?\w+;/g, entityReplacer);
67514 locator && position(start);
67515 domBuilder.characters(xt, 0, end - start);
67520 function position(p, m) {
67521 while (p >= lineEnd && (m = linePattern.exec(source))) {
67522 lineStart = m.index;
67523 lineEnd = lineStart + m[0].length;
67524 locator.lineNumber++; //console.log('line++:',locator,startPos,endPos)
67527 locator.columnNumber = p - lineStart + 1;
67532 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
67533 var locator = domBuilder.locator;
67534 var parseStack = [{
67535 currentNSMap: defaultNSMapCopy
67542 var tagStart = source.indexOf('<', start);
67544 if (tagStart < 0) {
67545 if (!source.substr(start).match(/^\s*$/)) {
67546 var doc = domBuilder.doc;
67547 var text = doc.createTextNode(source.substr(start));
67548 doc.appendChild(text);
67549 domBuilder.currentElement = text;
67555 if (tagStart > start) {
67556 appendText(tagStart);
67559 switch (source.charAt(tagStart + 1)) {
67561 var end = source.indexOf('>', tagStart + 3);
67562 var tagName = source.substring(tagStart + 2, end);
67563 var config = parseStack.pop();
67566 tagName = source.substring(tagStart + 2).replace(/[\s<].*/, ''); //console.error('#@@@@@@'+tagName)
67568 errorHandler.error("end tag name: " + tagName + ' is not complete:' + config.tagName);
67569 end = tagStart + 1 + tagName.length;
67570 } else if (tagName.match(/\s</)) {
67571 tagName = tagName.replace(/[\s<].*/, '');
67572 errorHandler.error("end tag name: " + tagName + ' maybe not complete');
67573 end = tagStart + 1 + tagName.length;
67574 } //console.error(parseStack.length,parseStack)
67575 //console.error(config);
67578 var localNSMap = config.localNSMap;
67579 var endMatch = config.tagName == tagName;
67580 var endIgnoreCaseMach = endMatch || config.tagName && config.tagName.toLowerCase() == tagName.toLowerCase();
67582 if (endIgnoreCaseMach) {
67583 domBuilder.endElement(config.uri, config.localName, tagName);
67586 for (var prefix in localNSMap) {
67587 domBuilder.endPrefixMapping(prefix);
67592 errorHandler.fatalError("end tag name: " + tagName + ' is not match the current start tagName:' + config.tagName);
67595 parseStack.push(config);
67604 locator && position(tagStart);
67605 end = parseInstruction(source, tagStart, domBuilder);
67609 // <!doctype,<![CDATA,<!--
67610 locator && position(tagStart);
67611 end = parseDCC(source, tagStart, domBuilder, errorHandler);
67615 locator && position(tagStart);
67616 var el = new ElementAttributes();
67617 var currentNSMap = parseStack[parseStack.length - 1].currentNSMap; //elStartEnd
67619 var end = parseElementStartPart(source, tagStart, el, currentNSMap, entityReplacer, errorHandler);
67620 var len = el.length;
67622 if (!el.closed && fixSelfClosed(source, end, el.tagName, closeMap)) {
67625 if (!entityMap.nbsp) {
67626 errorHandler.warning('unclosed xml attribute');
67630 if (locator && len) {
67631 var locator2 = copyLocator(locator, {}); //try{//attribute position fixed
67633 for (var i = 0; i < len; i++) {
67635 position(a.offset);
67636 a.locator = copyLocator(locator, {});
67637 } //}catch(e){console.error('@@@@@'+e)}
67640 domBuilder.locator = locator2;
67642 if (appendElement(el, domBuilder, currentNSMap)) {
67643 parseStack.push(el);
67646 domBuilder.locator = locator;
67648 if (appendElement(el, domBuilder, currentNSMap)) {
67649 parseStack.push(el);
67653 if (el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed) {
67654 end = parseHtmlSpecialContent(source, end, el.tagName, entityReplacer, domBuilder);
67661 errorHandler.error('element parse error: ' + e); //errorHandler.error('element parse error: '+e);
67663 end = -1; //throw e;
67669 //TODO: 这里有可能sax回退,有位置错误风险
67670 appendText(Math.max(tagStart, start) + 1);
67675 function copyLocator(f, t) {
67676 t.lineNumber = f.lineNumber;
67677 t.columnNumber = f.columnNumber;
67681 * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
67682 * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
67686 function parseElementStartPart(source, start, el, currentNSMap, entityReplacer, errorHandler) {
67690 var s = S_TAG; //status
67693 var c = source.charAt(p);
67697 if (s === S_ATTR) {
67699 attrName = source.slice(start, p);
67701 } else if (s === S_ATTR_SPACE) {
67704 //fatalError: equal must after attrName or space after attrName
67705 throw new Error('attribute equal must after attrName');
67712 if (s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
67715 if (s === S_ATTR) {
67716 errorHandler.warning('attribute value must after "="');
67717 attrName = source.slice(start, p);
67721 p = source.indexOf(c, start);
67724 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
67725 el.add(attrName, value, start - 1);
67728 //fatalError: no end quot match
67729 throw new Error('attribute value no end \'' + c + '\' match');
67731 } else if (s == S_ATTR_NOQUOT_VALUE) {
67732 value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer); //console.log(attrName,value,start,p)
67734 el.add(attrName, value, start); //console.dir(el)
67736 errorHandler.warning('attribute "' + attrName + '" missed start quot(' + c + ')!!');
67740 //fatalError: no equal before
67741 throw new Error('attribute value must after "="');
67749 el.setTagName(source.slice(start, p));
67757 case S_ATTR_NOQUOT_VALUE:
67764 throw new Error("attribute invalid close char('/')");
67771 //throw new Error('unexpected end of input')
67772 errorHandler.error('unexpected end of input');
67775 el.setTagName(source.slice(start, p));
67783 el.setTagName(source.slice(start, p));
67791 case S_ATTR_NOQUOT_VALUE: //Compatible state
67794 value = source.slice(start, p);
67796 if (value.slice(-1) === '/') {
67798 value = value.slice(0, -1);
67802 if (s === S_ATTR_SPACE) {
67806 if (s == S_ATTR_NOQUOT_VALUE) {
67807 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
67808 el.add(attrName, value.replace(/&#?\w+;/g, entityReplacer), start);
67810 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)) {
67811 errorHandler.warning('attribute "' + value + '" missed value!! "' + value + '" instead!!');
67814 el.add(value, value, start);
67820 throw new Error('attribute value missed!!');
67821 } // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
67826 /*xml space '\x20' | #x9 | #xD | #xA; */
67836 el.setTagName(source.slice(start, p)); //tagName
67842 attrName = source.slice(start, p);
67846 case S_ATTR_NOQUOT_VALUE:
67847 var value = source.slice(start, p).replace(/&#?\w+;/g, entityReplacer);
67848 errorHandler.warning('attribute "' + value + '" missed quot(")!!');
67849 el.add(attrName, value, start);
67854 //case S_TAG_SPACE:
67856 //case S_ATTR_SPACE:
67858 //case S_TAG_CLOSE:
67863 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE
67864 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE
67866 //case S_TAG:void();break;
67867 //case S_ATTR:void();break;
67868 //case S_ATTR_NOQUOT_VALUE:void();break;
67870 var tagName = el.tagName;
67872 if (currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)) {
67873 errorHandler.warning('attribute "' + attrName + '" missed value!! "' + attrName + '" instead2!!');
67876 el.add(attrName, attrName, start);
67882 errorHandler.warning('attribute space is required"' + attrName + '"!!');
67890 s = S_ATTR_NOQUOT_VALUE;
67895 throw new Error("elements closed character '/' and '>' must be connected to");
67899 } //end outer switch
67900 //console.log('p++',p)
67907 * @return true if has new namespace define
67911 function appendElement(el, domBuilder, currentNSMap) {
67912 var tagName = el.tagName;
67913 var localNSMap = null; //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
67919 var qName = a.qName;
67920 var value = a.value;
67921 var nsp = qName.indexOf(':');
67924 var prefix = a.prefix = qName.slice(0, nsp);
67925 var localName = qName.slice(nsp + 1);
67926 var nsPrefix = prefix === 'xmlns' && localName;
67930 nsPrefix = qName === 'xmlns' && '';
67931 } //can not set prefix,because prefix !== ''
67934 a.localName = localName; //prefix == null for no ns prefix attribute
67936 if (nsPrefix !== false) {
67938 if (localNSMap == null) {
67939 localNSMap = {}; //console.log(currentNSMap,0)
67941 _copy(currentNSMap, currentNSMap = {}); //console.log(currentNSMap,1)
67945 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
67946 a.uri = 'http://www.w3.org/2000/xmlns/';
67947 domBuilder.startPrefixMapping(nsPrefix, value);
67955 var prefix = a.prefix;
67958 //no prefix attribute has no namespace
67959 if (prefix === 'xml') {
67960 a.uri = 'http://www.w3.org/XML/1998/namespace';
67963 if (prefix !== 'xmlns') {
67964 a.uri = currentNSMap[prefix || '']; //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
67969 var nsp = tagName.indexOf(':');
67972 prefix = el.prefix = tagName.slice(0, nsp);
67973 localName = el.localName = tagName.slice(nsp + 1);
67975 prefix = null; //important!!
67977 localName = el.localName = tagName;
67978 } //no prefix element has default namespace
67981 var ns = el.uri = currentNSMap[prefix || ''];
67982 domBuilder.startElement(ns, localName, tagName, el); //endPrefixMapping and startPrefixMapping have not any help for dom builder
67983 //localNSMap = null
67986 domBuilder.endElement(ns, localName, tagName);
67989 for (prefix in localNSMap) {
67990 domBuilder.endPrefixMapping(prefix);
67994 el.currentNSMap = currentNSMap;
67995 el.localNSMap = localNSMap; //parseStack.push(el);
68001 function parseHtmlSpecialContent(source, elStartEnd, tagName, entityReplacer, domBuilder) {
68002 if (/^(?:script|textarea)$/i.test(tagName)) {
68003 var elEndStart = source.indexOf('</' + tagName + '>', elStartEnd);
68004 var text = source.substring(elStartEnd + 1, elEndStart);
68006 if (/[&<]/.test(text)) {
68007 if (/^script$/i.test(tagName)) {
68008 //if(!/\]\]>/.test(text)){
68009 //lexHandler.startCDATA();
68010 domBuilder.characters(text, 0, text.length); //lexHandler.endCDATA();
68012 return elEndStart; //}
68013 } //}else{//text area
68016 text = text.replace(/&#?\w+;/g, entityReplacer);
68017 domBuilder.characters(text, 0, text.length);
68018 return elEndStart; //}
68022 return elStartEnd + 1;
68025 function fixSelfClosed(source, elStartEnd, tagName, closeMap) {
68026 //if(tagName in closeMap){
68027 var pos = closeMap[tagName];
68030 //console.log(tagName)
68031 pos = source.lastIndexOf('</' + tagName + '>');
68033 if (pos < elStartEnd) {
68035 pos = source.lastIndexOf('</' + tagName);
68038 closeMap[tagName] = pos;
68041 return pos < elStartEnd; //}
68044 function _copy(source, target) {
68045 for (var n in source) {
68046 target[n] = source[n];
68050 function parseDCC(source, start, domBuilder, errorHandler) {
68051 //sure start with '<!'
68052 var next = source.charAt(start + 2);
68056 if (source.charAt(start + 3) === '-') {
68057 var end = source.indexOf('-->', start + 4); //append comment source.substring(4,end)//<!--
68060 domBuilder.comment(source, start + 4, end - start - 4);
68063 errorHandler.error("Unclosed comment");
68072 if (source.substr(start + 3, 6) == 'CDATA[') {
68073 var end = source.indexOf(']]>', start + 9);
68074 domBuilder.startCDATA();
68075 domBuilder.characters(source, start + 9, end - start - 9);
68076 domBuilder.endCDATA();
68079 //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
68082 var matchs = split$1(source, start);
68083 var len = matchs.length;
68085 if (len > 1 && /!doctype/i.test(matchs[0][0])) {
68086 var name = matchs[1][0];
68087 var pubid = len > 3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
68088 var sysid = len > 4 && matchs[4][0];
68089 var lastMatch = matchs[len - 1];
68090 domBuilder.startDTD(name, pubid && pubid.replace(/^(['"])(.*?)\1$/, '$2'), sysid && sysid.replace(/^(['"])(.*?)\1$/, '$2'));
68091 domBuilder.endDTD();
68092 return lastMatch.index + lastMatch[0].length;
68100 function parseInstruction(source, start, domBuilder) {
68101 var end = source.indexOf('?>', start);
68104 var match = source.substring(start, end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
68107 var len = match[0].length;
68108 domBuilder.processingInstruction(match[1], match[2]);
68123 function ElementAttributes(source) {}
68125 ElementAttributes.prototype = {
68126 setTagName: function setTagName(tagName) {
68127 if (!tagNamePattern.test(tagName)) {
68128 throw new Error('invalid tagName:' + tagName);
68131 this.tagName = tagName;
68133 add: function add(qName, value, offset) {
68134 if (!tagNamePattern.test(qName)) {
68135 throw new Error('invalid attribute:' + qName);
68138 this[this.length++] = {
68145 getLocalName: function getLocalName(i) {
68146 return this[i].localName;
68148 getLocator: function getLocator(i) {
68149 return this[i].locator;
68151 getQName: function getQName(i) {
68152 return this[i].qName;
68154 getURI: function getURI(i) {
68155 return this[i].uri;
68157 getValue: function getValue(i) {
68158 return this[i].value;
68159 } // ,getIndex:function(uri, localName)){
68166 // getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
68167 // getType:function(uri,localName){}
68168 // getType:function(i){},
68172 function _set_proto_(thiz, parent) {
68173 thiz.__proto__ = parent;
68177 if (!(_set_proto_({}, _set_proto_.prototype) instanceof _set_proto_)) {
68178 _set_proto_ = function _set_proto_(thiz, parent) {
68180 p.prototype = parent;
68183 for (parent in thiz) {
68184 p[parent] = thiz[parent];
68191 function split$1(source, start) {
68194 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
68195 reg.lastIndex = start;
68196 reg.exec(source); //skip <
68198 while (match = reg.exec(source)) {
68200 if (match[1]) return buf;
68204 var XMLReader_1 = XMLReader;
68206 XMLReader: XMLReader_1
68211 * Object DOMException
68212 * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
68213 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
68215 function copy$1(src, dest) {
68216 for (var p in src) {
68221 ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
68222 ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
68226 function _extends(Class, Super) {
68227 var pt = Class.prototype;
68229 if (Object.create) {
68230 var ppt = Object.create(Super.prototype);
68231 pt.__proto__ = ppt;
68234 if (!(pt instanceof Super)) {
68235 var t = function t() {};
68236 t.prototype = Super.prototype;
68239 Class.prototype = pt = t;
68242 if (pt.constructor != Class) {
68243 if (typeof Class != 'function') {
68244 console.error("unknow Class:" + Class);
68247 pt.constructor = Class;
68251 var htmlns = 'http://www.w3.org/1999/xhtml'; // Node Types
68254 var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1;
68255 var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2;
68256 var TEXT_NODE = NodeType.TEXT_NODE = 3;
68257 var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4;
68258 var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5;
68259 var ENTITY_NODE = NodeType.ENTITY_NODE = 6;
68260 var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
68261 var COMMENT_NODE = NodeType.COMMENT_NODE = 8;
68262 var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9;
68263 var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10;
68264 var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11;
68265 var NOTATION_NODE = NodeType.NOTATION_NODE = 12; // ExceptionCode
68267 var ExceptionCode = {};
68268 var ExceptionMessage = {};
68269 var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = (ExceptionMessage[1] = "Index size error", 1);
68270 var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = (ExceptionMessage[2] = "DOMString size error", 2);
68271 var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = (ExceptionMessage[3] = "Hierarchy request error", 3);
68272 var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = (ExceptionMessage[4] = "Wrong document", 4);
68273 var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = (ExceptionMessage[5] = "Invalid character", 5);
68274 var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = (ExceptionMessage[6] = "No data allowed", 6);
68275 var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = (ExceptionMessage[7] = "No modification allowed", 7);
68276 var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = (ExceptionMessage[8] = "Not found", 8);
68277 var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = (ExceptionMessage[9] = "Not supported", 9);
68278 var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = (ExceptionMessage[10] = "Attribute in use", 10); //level2
68280 var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = (ExceptionMessage[11] = "Invalid state", 11);
68281 var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = (ExceptionMessage[12] = "Syntax error", 12);
68282 var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = (ExceptionMessage[13] = "Invalid modification", 13);
68283 var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = (ExceptionMessage[14] = "Invalid namespace", 14);
68284 var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = (ExceptionMessage[15] = "Invalid access", 15);
68286 function DOMException$2(code, message) {
68287 if (message instanceof Error) {
68288 var error = message;
68291 Error.call(this, ExceptionMessage[code]);
68292 this.message = ExceptionMessage[code];
68293 if (Error.captureStackTrace) Error.captureStackTrace(this, DOMException$2);
68297 if (message) this.message = this.message + ": " + message;
68300 DOMException$2.prototype = Error.prototype;
68301 copy$1(ExceptionCode, DOMException$2);
68303 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
68304 * 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.
68305 * The items in the NodeList are accessible via an integral index, starting from 0.
68308 function NodeList() {}
68309 NodeList.prototype = {
68311 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
68317 * 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.
68319 * @param index unsigned long
68320 * Index into the collection.
68322 * The node at the indexth position in the NodeList, or null if that is not a valid index.
68324 item: function item(index) {
68325 return this[index] || null;
68327 toString: function toString(isHTML, nodeFilter) {
68328 for (var buf = [], i = 0; i < this.length; i++) {
68329 serializeToString(this[i], buf, isHTML, nodeFilter);
68332 return buf.join('');
68336 function LiveNodeList(node, refresh) {
68338 this._refresh = refresh;
68340 _updateLiveList(this);
68343 function _updateLiveList(list) {
68344 var inc = list._node._inc || list._node.ownerDocument._inc;
68346 if (list._inc != inc) {
68347 var ls = list._refresh(list._node); //console.log(ls.length)
68350 __set__(list, 'length', ls.length);
68357 LiveNodeList.prototype.item = function (i) {
68358 _updateLiveList(this);
68363 _extends(LiveNodeList, NodeList);
68366 * 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.
68367 * NamedNodeMap objects in the DOM are live.
68368 * used for attributes or DocumentType entities
68372 function NamedNodeMap() {}
68374 function _findNodeIndex(list, node) {
68375 var i = list.length;
68378 if (list[i] === node) {
68384 function _addNamedNode(el, list, newAttr, oldAttr) {
68386 list[_findNodeIndex(list, oldAttr)] = newAttr;
68388 list[list.length++] = newAttr;
68392 newAttr.ownerElement = el;
68393 var doc = el.ownerDocument;
68396 oldAttr && _onRemoveAttribute(doc, el, oldAttr);
68398 _onAddAttribute(doc, el, newAttr);
68403 function _removeNamedNode(el, list, attr) {
68404 //console.log('remove attr:'+attr)
68405 var i = _findNodeIndex(list, attr);
68408 var lastIndex = list.length - 1;
68410 while (i < lastIndex) {
68411 list[i] = list[++i];
68414 list.length = lastIndex;
68417 var doc = el.ownerDocument;
68420 _onRemoveAttribute(doc, el, attr);
68422 attr.ownerElement = null;
68426 throw DOMException$2(NOT_FOUND_ERR, new Error(el.tagName + '@' + attr));
68430 NamedNodeMap.prototype = {
68432 item: NodeList.prototype.item,
68433 getNamedItem: function getNamedItem(key) {
68434 // if(key.indexOf(':')>0 || key == 'xmlns'){
68438 var i = this.length;
68441 var attr = this[i]; //console.log(attr.nodeName,key)
68443 if (attr.nodeName == key) {
68448 setNamedItem: function setNamedItem(attr) {
68449 var el = attr.ownerElement;
68451 if (el && el != this._ownerElement) {
68452 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
68455 var oldAttr = this.getNamedItem(attr.nodeName);
68457 _addNamedNode(this._ownerElement, this, attr, oldAttr);
68463 setNamedItemNS: function setNamedItemNS(attr) {
68464 // raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
68465 var el = attr.ownerElement,
68468 if (el && el != this._ownerElement) {
68469 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
68472 oldAttr = this.getNamedItemNS(attr.namespaceURI, attr.localName);
68474 _addNamedNode(this._ownerElement, this, attr, oldAttr);
68480 removeNamedItem: function removeNamedItem(key) {
68481 var attr = this.getNamedItem(key);
68483 _removeNamedNode(this._ownerElement, this, attr);
68487 // raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
68489 removeNamedItemNS: function removeNamedItemNS(namespaceURI, localName) {
68490 var attr = this.getNamedItemNS(namespaceURI, localName);
68492 _removeNamedNode(this._ownerElement, this, attr);
68496 getNamedItemNS: function getNamedItemNS(namespaceURI, localName) {
68497 var i = this.length;
68500 var node = this[i];
68502 if (node.localName == localName && node.namespaceURI == namespaceURI) {
68511 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
68514 function DOMImplementation(
68517 this._features = {};
68520 for (var feature in features) {
68521 this._features = features[feature];
68525 DOMImplementation.prototype = {
68526 hasFeature: function hasFeature(
68531 var versions = this._features[feature.toLowerCase()];
68533 if (versions && (!version || version in versions)) {
68539 // Introduced in DOM Level 2:
68540 createDocument: function createDocument(namespaceURI, qualifiedName, doctype) {
68541 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
68542 var doc = new Document();
68543 doc.implementation = this;
68544 doc.childNodes = new NodeList();
68545 doc.doctype = doctype;
68548 doc.appendChild(doctype);
68551 if (qualifiedName) {
68552 var root = doc.createElementNS(namespaceURI, qualifiedName);
68553 doc.appendChild(root);
68558 // Introduced in DOM Level 2:
68559 createDocumentType: function createDocumentType(qualifiedName, publicId, systemId) {
68560 // raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
68561 var node = new DocumentType();
68562 node.name = qualifiedName;
68563 node.nodeName = qualifiedName;
68564 node.publicId = publicId;
68565 node.systemId = systemId; // Introduced in DOM Level 2:
68566 //readonly attribute DOMString internalSubset;
68568 // readonly attribute NamedNodeMap entities;
68569 // readonly attribute NamedNodeMap notations;
68575 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
68582 previousSibling: null,
68587 ownerDocument: null,
68589 namespaceURI: null,
68592 // Modified in DOM Level 2:
68593 insertBefore: function insertBefore(newChild, refChild) {
68595 return _insertBefore(this, newChild, refChild);
68597 replaceChild: function replaceChild(newChild, oldChild) {
68599 this.insertBefore(newChild, oldChild);
68602 this.removeChild(oldChild);
68605 removeChild: function removeChild(oldChild) {
68606 return _removeChild(this, oldChild);
68608 appendChild: function appendChild(newChild) {
68609 return this.insertBefore(newChild, null);
68611 hasChildNodes: function hasChildNodes() {
68612 return this.firstChild != null;
68614 cloneNode: function cloneNode(deep) {
68615 return _cloneNode(this.ownerDocument || this, this, deep);
68617 // Modified in DOM Level 2:
68618 normalize: function normalize() {
68619 var child = this.firstChild;
68622 var next = child.nextSibling;
68624 if (next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE) {
68625 this.removeChild(next);
68626 child.appendData(next.data);
68633 // Introduced in DOM Level 2:
68634 isSupported: function isSupported(feature, version) {
68635 return this.ownerDocument.implementation.hasFeature(feature, version);
68637 // Introduced in DOM Level 2:
68638 hasAttributes: function hasAttributes() {
68639 return this.attributes.length > 0;
68641 lookupPrefix: function lookupPrefix(namespaceURI) {
68645 var map = el._nsMap; //console.dir(map)
68648 for (var n in map) {
68649 if (map[n] == namespaceURI) {
68655 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
68660 // Introduced in DOM Level 3:
68661 lookupNamespaceURI: function lookupNamespaceURI(prefix) {
68665 var map = el._nsMap; //console.dir(map)
68668 if (prefix in map) {
68669 return map[prefix];
68673 el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
68678 // Introduced in DOM Level 3:
68679 isDefaultNamespace: function isDefaultNamespace(namespaceURI) {
68680 var prefix = this.lookupPrefix(namespaceURI);
68681 return prefix == null;
68685 function _xmlEncoder(c) {
68686 return c == '<' && '<' || c == '>' && '>' || c == '&' && '&' || c == '"' && '"' || '&#' + c.charCodeAt() + ';';
68689 copy$1(NodeType, Node);
68690 copy$1(NodeType, Node.prototype);
68692 * @param callback return true for continue,false for break
68693 * @return boolean true: break visit;
68696 function _visitNode(node, callback) {
68697 if (callback(node)) {
68701 if (node = node.firstChild) {
68703 if (_visitNode(node, callback)) {
68706 } while (node = node.nextSibling);
68710 function Document() {}
68712 function _onAddAttribute(doc, el, newAttr) {
68714 var ns = newAttr.namespaceURI;
68716 if (ns == 'http://www.w3.org/2000/xmlns/') {
68718 el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
68722 function _onRemoveAttribute(doc, el, newAttr, remove) {
68724 var ns = newAttr.namespaceURI;
68726 if (ns == 'http://www.w3.org/2000/xmlns/') {
68728 delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
68732 function _onUpdateChild(doc, el, newChild) {
68733 if (doc && doc._inc) {
68734 doc._inc++; //update childNodes
68736 var cs = el.childNodes;
68739 cs[cs.length++] = newChild;
68742 var child = el.firstChild;
68747 child = child.nextSibling;
68758 * writeable properties:
68759 * nodeValue,Attr:value,CharacterData:data
68764 function _removeChild(parentNode, child) {
68765 var previous = child.previousSibling;
68766 var next = child.nextSibling;
68769 previous.nextSibling = next;
68771 parentNode.firstChild = next;
68775 next.previousSibling = previous;
68777 parentNode.lastChild = previous;
68780 _onUpdateChild(parentNode.ownerDocument, parentNode);
68785 * preformance key(refChild == null)
68789 function _insertBefore(parentNode, newChild, nextChild) {
68790 var cp = newChild.parentNode;
68793 cp.removeChild(newChild); //remove and update
68796 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
68797 var newFirst = newChild.firstChild;
68799 if (newFirst == null) {
68803 var newLast = newChild.lastChild;
68805 newFirst = newLast = newChild;
68808 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
68809 newFirst.previousSibling = pre;
68810 newLast.nextSibling = nextChild;
68813 pre.nextSibling = newFirst;
68815 parentNode.firstChild = newFirst;
68818 if (nextChild == null) {
68819 parentNode.lastChild = newLast;
68821 nextChild.previousSibling = newLast;
68825 newFirst.parentNode = parentNode;
68826 } while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
68828 _onUpdateChild(parentNode.ownerDocument || parentNode, parentNode); //console.log(parentNode.lastChild.nextSibling == null)
68831 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
68832 newChild.firstChild = newChild.lastChild = null;
68838 function _appendSingleChild(parentNode, newChild) {
68839 var cp = newChild.parentNode;
68842 var pre = parentNode.lastChild;
68843 cp.removeChild(newChild); //remove and update
68845 var pre = parentNode.lastChild;
68848 var pre = parentNode.lastChild;
68849 newChild.parentNode = parentNode;
68850 newChild.previousSibling = pre;
68851 newChild.nextSibling = null;
68854 pre.nextSibling = newChild;
68856 parentNode.firstChild = newChild;
68859 parentNode.lastChild = newChild;
68861 _onUpdateChild(parentNode.ownerDocument, parentNode, newChild);
68863 return newChild; //console.log("__aa",parentNode.lastChild.nextSibling == null)
68866 Document.prototype = {
68867 //implementation : null,
68868 nodeName: '#document',
68869 nodeType: DOCUMENT_NODE,
68871 documentElement: null,
68873 insertBefore: function insertBefore(newChild, refChild) {
68875 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
68876 var child = newChild.firstChild;
68879 var next = child.nextSibling;
68880 this.insertBefore(child, refChild);
68887 if (this.documentElement == null && newChild.nodeType == ELEMENT_NODE) {
68888 this.documentElement = newChild;
68891 return _insertBefore(this, newChild, refChild), newChild.ownerDocument = this, newChild;
68893 removeChild: function removeChild(oldChild) {
68894 if (this.documentElement == oldChild) {
68895 this.documentElement = null;
68898 return _removeChild(this, oldChild);
68900 // Introduced in DOM Level 2:
68901 importNode: function importNode(importedNode, deep) {
68902 return _importNode(this, importedNode, deep);
68904 // Introduced in DOM Level 2:
68905 getElementById: function getElementById(id) {
68908 _visitNode(this.documentElement, function (node) {
68909 if (node.nodeType == ELEMENT_NODE) {
68910 if (node.getAttribute('id') == id) {
68919 //document factory method:
68920 createElement: function createElement(tagName) {
68921 var node = new Element();
68922 node.ownerDocument = this;
68923 node.nodeName = tagName;
68924 node.tagName = tagName;
68925 node.childNodes = new NodeList();
68926 var attrs = node.attributes = new NamedNodeMap();
68927 attrs._ownerElement = node;
68930 createDocumentFragment: function createDocumentFragment() {
68931 var node = new DocumentFragment();
68932 node.ownerDocument = this;
68933 node.childNodes = new NodeList();
68936 createTextNode: function createTextNode(data) {
68937 var node = new Text();
68938 node.ownerDocument = this;
68939 node.appendData(data);
68942 createComment: function createComment(data) {
68943 var node = new Comment();
68944 node.ownerDocument = this;
68945 node.appendData(data);
68948 createCDATASection: function createCDATASection(data) {
68949 var node = new CDATASection();
68950 node.ownerDocument = this;
68951 node.appendData(data);
68954 createProcessingInstruction: function createProcessingInstruction(target, data) {
68955 var node = new ProcessingInstruction();
68956 node.ownerDocument = this;
68957 node.tagName = node.target = target;
68958 node.nodeValue = node.data = data;
68961 createAttribute: function createAttribute(name) {
68962 var node = new Attr();
68963 node.ownerDocument = this;
68965 node.nodeName = name;
68966 node.localName = name;
68967 node.specified = true;
68970 createEntityReference: function createEntityReference(name) {
68971 var node = new EntityReference();
68972 node.ownerDocument = this;
68973 node.nodeName = name;
68976 // Introduced in DOM Level 2:
68977 createElementNS: function createElementNS(namespaceURI, qualifiedName) {
68978 var node = new Element();
68979 var pl = qualifiedName.split(':');
68980 var attrs = node.attributes = new NamedNodeMap();
68981 node.childNodes = new NodeList();
68982 node.ownerDocument = this;
68983 node.nodeName = qualifiedName;
68984 node.tagName = qualifiedName;
68985 node.namespaceURI = namespaceURI;
68987 if (pl.length == 2) {
68988 node.prefix = pl[0];
68989 node.localName = pl[1];
68991 //el.prefix = null;
68992 node.localName = qualifiedName;
68995 attrs._ownerElement = node;
68998 // Introduced in DOM Level 2:
68999 createAttributeNS: function createAttributeNS(namespaceURI, qualifiedName) {
69000 var node = new Attr();
69001 var pl = qualifiedName.split(':');
69002 node.ownerDocument = this;
69003 node.nodeName = qualifiedName;
69004 node.name = qualifiedName;
69005 node.namespaceURI = namespaceURI;
69006 node.specified = true;
69008 if (pl.length == 2) {
69009 node.prefix = pl[0];
69010 node.localName = pl[1];
69012 //el.prefix = null;
69013 node.localName = qualifiedName;
69020 _extends(Document, Node);
69022 function Element() {
69025 Element.prototype = {
69026 nodeType: ELEMENT_NODE,
69027 hasAttribute: function hasAttribute(name) {
69028 return this.getAttributeNode(name) != null;
69030 getAttribute: function getAttribute(name) {
69031 var attr = this.getAttributeNode(name);
69032 return attr && attr.value || '';
69034 getAttributeNode: function getAttributeNode(name) {
69035 return this.attributes.getNamedItem(name);
69037 setAttribute: function setAttribute(name, value) {
69038 var attr = this.ownerDocument.createAttribute(name);
69039 attr.value = attr.nodeValue = "" + value;
69040 this.setAttributeNode(attr);
69042 removeAttribute: function removeAttribute(name) {
69043 var attr = this.getAttributeNode(name);
69044 attr && this.removeAttributeNode(attr);
69046 //four real opeartion method
69047 appendChild: function appendChild(newChild) {
69048 if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
69049 return this.insertBefore(newChild, null);
69051 return _appendSingleChild(this, newChild);
69054 setAttributeNode: function setAttributeNode(newAttr) {
69055 return this.attributes.setNamedItem(newAttr);
69057 setAttributeNodeNS: function setAttributeNodeNS(newAttr) {
69058 return this.attributes.setNamedItemNS(newAttr);
69060 removeAttributeNode: function removeAttributeNode(oldAttr) {
69061 //console.log(this == oldAttr.ownerElement)
69062 return this.attributes.removeNamedItem(oldAttr.nodeName);
69064 //get real attribute name,and remove it by removeAttributeNode
69065 removeAttributeNS: function removeAttributeNS(namespaceURI, localName) {
69066 var old = this.getAttributeNodeNS(namespaceURI, localName);
69067 old && this.removeAttributeNode(old);
69069 hasAttributeNS: function hasAttributeNS(namespaceURI, localName) {
69070 return this.getAttributeNodeNS(namespaceURI, localName) != null;
69072 getAttributeNS: function getAttributeNS(namespaceURI, localName) {
69073 var attr = this.getAttributeNodeNS(namespaceURI, localName);
69074 return attr && attr.value || '';
69076 setAttributeNS: function setAttributeNS(namespaceURI, qualifiedName, value) {
69077 var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
69078 attr.value = attr.nodeValue = "" + value;
69079 this.setAttributeNode(attr);
69081 getAttributeNodeNS: function getAttributeNodeNS(namespaceURI, localName) {
69082 return this.attributes.getNamedItemNS(namespaceURI, localName);
69084 getElementsByTagName: function getElementsByTagName(tagName) {
69085 return new LiveNodeList(this, function (base) {
69088 _visitNode(base, function (node) {
69089 if (node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)) {
69097 getElementsByTagNameNS: function getElementsByTagNameNS(namespaceURI, localName) {
69098 return new LiveNodeList(this, function (base) {
69101 _visitNode(base, function (node) {
69102 if (node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)) {
69111 Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
69112 Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
69114 _extends(Element, Node);
69117 Attr.prototype.nodeType = ATTRIBUTE_NODE;
69119 _extends(Attr, Node);
69121 function CharacterData() {}
69122 CharacterData.prototype = {
69124 substringData: function substringData(offset, count) {
69125 return this.data.substring(offset, offset + count);
69127 appendData: function appendData(text) {
69128 text = this.data + text;
69129 this.nodeValue = this.data = text;
69130 this.length = text.length;
69132 insertData: function insertData(offset, text) {
69133 this.replaceData(offset, 0, text);
69135 appendChild: function appendChild(newChild) {
69136 throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]);
69138 deleteData: function deleteData(offset, count) {
69139 this.replaceData(offset, count, "");
69141 replaceData: function replaceData(offset, count, text) {
69142 var start = this.data.substring(0, offset);
69143 var end = this.data.substring(offset + count);
69144 text = start + text + end;
69145 this.nodeValue = this.data = text;
69146 this.length = text.length;
69150 _extends(CharacterData, Node);
69155 nodeType: TEXT_NODE,
69156 splitText: function splitText(offset) {
69157 var text = this.data;
69158 var newText = text.substring(offset);
69159 text = text.substring(0, offset);
69160 this.data = this.nodeValue = text;
69161 this.length = text.length;
69162 var newNode = this.ownerDocument.createTextNode(newText);
69164 if (this.parentNode) {
69165 this.parentNode.insertBefore(newNode, this.nextSibling);
69172 _extends(Text, CharacterData);
69174 function Comment() {}
69175 Comment.prototype = {
69176 nodeName: "#comment",
69177 nodeType: COMMENT_NODE
69180 _extends(Comment, CharacterData);
69182 function CDATASection() {}
69183 CDATASection.prototype = {
69184 nodeName: "#cdata-section",
69185 nodeType: CDATA_SECTION_NODE
69188 _extends(CDATASection, CharacterData);
69190 function DocumentType() {}
69191 DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
69193 _extends(DocumentType, Node);
69195 function Notation() {}
69196 Notation.prototype.nodeType = NOTATION_NODE;
69198 _extends(Notation, Node);
69200 function Entity() {}
69201 Entity.prototype.nodeType = ENTITY_NODE;
69203 _extends(Entity, Node);
69205 function EntityReference() {}
69206 EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
69208 _extends(EntityReference, Node);
69210 function DocumentFragment() {}
69211 DocumentFragment.prototype.nodeName = "#document-fragment";
69212 DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
69214 _extends(DocumentFragment, Node);
69216 function ProcessingInstruction() {}
69218 ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
69220 _extends(ProcessingInstruction, Node);
69222 function XMLSerializer$1() {}
69224 XMLSerializer$1.prototype.serializeToString = function (node, isHtml, nodeFilter) {
69225 return nodeSerializeToString.call(node, isHtml, nodeFilter);
69228 Node.prototype.toString = nodeSerializeToString;
69230 function nodeSerializeToString(isHtml, nodeFilter) {
69232 var refNode = this.nodeType == 9 ? this.documentElement : this;
69233 var prefix = refNode.prefix;
69234 var uri = refNode.namespaceURI;
69236 if (uri && prefix == null) {
69237 //console.log(prefix)
69238 var prefix = refNode.lookupPrefix(uri);
69240 if (prefix == null) {
69242 var visibleNamespaces = [{
69245 } //{namespace:uri,prefix:''}
69250 serializeToString(this, buf, isHtml, nodeFilter, visibleNamespaces); //console.log('###',this.nodeType,uri,prefix,buf.join(''))
69252 return buf.join('');
69255 function needNamespaceDefine(node, isHTML, visibleNamespaces) {
69256 var prefix = node.prefix || '';
69257 var uri = node.namespaceURI;
69259 if (!prefix && !uri) {
69263 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" || uri == 'http://www.w3.org/2000/xmlns/') {
69267 var i = visibleNamespaces.length; //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
69270 var ns = visibleNamespaces[i]; // get namespace prefix
69271 //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
69273 if (ns.prefix == prefix) {
69274 return ns.namespace != uri;
69276 } //console.log(isHTML,uri,prefix=='')
69277 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
69280 //node.flag = '11111'
69281 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
69287 function serializeToString(node, buf, isHTML, nodeFilter, visibleNamespaces) {
69289 node = nodeFilter(node);
69292 if (typeof node == 'string') {
69298 } //buf.sort.apply(attrs, attributeSorter);
69302 switch (node.nodeType) {
69304 if (!visibleNamespaces) visibleNamespaces = [];
69305 var startVisibleNamespaces = visibleNamespaces.length;
69306 var attrs = node.attributes;
69307 var len = attrs.length;
69308 var child = node.firstChild;
69309 var nodeName = node.tagName;
69310 isHTML = htmlns === node.namespaceURI || isHTML;
69311 buf.push('<', nodeName);
69313 for (var i = 0; i < len; i++) {
69314 // add namespaces for attributes
69315 var attr = attrs.item(i);
69317 if (attr.prefix == 'xmlns') {
69318 visibleNamespaces.push({
69319 prefix: attr.localName,
69320 namespace: attr.value
69322 } else if (attr.nodeName == 'xmlns') {
69323 visibleNamespaces.push({
69325 namespace: attr.value
69330 for (var i = 0; i < len; i++) {
69331 var attr = attrs.item(i);
69333 if (needNamespaceDefine(attr, isHTML, visibleNamespaces)) {
69334 var prefix = attr.prefix || '';
69335 var uri = attr.namespaceURI;
69336 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
69337 buf.push(ns, '="', uri, '"');
69338 visibleNamespaces.push({
69344 serializeToString(attr, buf, isHTML, nodeFilter, visibleNamespaces);
69345 } // add namespace for current node
69348 if (needNamespaceDefine(node, isHTML, visibleNamespaces)) {
69349 var prefix = node.prefix || '';
69350 var uri = node.namespaceURI;
69351 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
69352 buf.push(ns, '="', uri, '"');
69353 visibleNamespaces.push({
69359 if (child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)) {
69360 buf.push('>'); //if is cdata child node
69362 if (isHTML && /^script$/i.test(nodeName)) {
69365 buf.push(child.data);
69367 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69370 child = child.nextSibling;
69374 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69375 child = child.nextSibling;
69379 buf.push('</', nodeName, '>');
69382 } // remove added visible namespaces
69383 //visibleNamespaces.length = startVisibleNamespaces;
69388 case DOCUMENT_NODE:
69389 case DOCUMENT_FRAGMENT_NODE:
69390 var child = node.firstChild;
69393 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces);
69394 child = child.nextSibling;
69399 case ATTRIBUTE_NODE:
69400 return buf.push(' ', node.name, '="', node.value.replace(/[<&"]/g, _xmlEncoder), '"');
69403 return buf.push(node.data.replace(/[<&]/g, _xmlEncoder));
69405 case CDATA_SECTION_NODE:
69406 return buf.push('<![CDATA[', node.data, ']]>');
69409 return buf.push("<!--", node.data, "-->");
69411 case DOCUMENT_TYPE_NODE:
69412 var pubid = node.publicId;
69413 var sysid = node.systemId;
69414 buf.push('<!DOCTYPE ', node.name);
69417 buf.push(' PUBLIC "', pubid);
69419 if (sysid && sysid != '.') {
69420 buf.push('" "', sysid);
69424 } else if (sysid && sysid != '.') {
69425 buf.push(' SYSTEM "', sysid, '">');
69427 var sub = node.internalSubset;
69430 buf.push(" [", sub, "]");
69438 case PROCESSING_INSTRUCTION_NODE:
69439 return buf.push("<?", node.target, " ", node.data, "?>");
69441 case ENTITY_REFERENCE_NODE:
69442 return buf.push('&', node.nodeName, ';');
69443 //case ENTITY_NODE:
69444 //case NOTATION_NODE:
69447 buf.push('??', node.nodeName);
69451 function _importNode(doc, node, deep) {
69454 switch (node.nodeType) {
69456 node2 = node.cloneNode(false);
69457 node2.ownerDocument = doc;
69458 //var attrs = node2.attributes;
69459 //var len = attrs.length;
69460 //for(var i=0;i<len;i++){
69461 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
69464 case DOCUMENT_FRAGMENT_NODE:
69467 case ATTRIBUTE_NODE:
69470 //case ENTITY_REFERENCE_NODE:
69471 //case PROCESSING_INSTRUCTION_NODE:
69472 ////case TEXT_NODE:
69473 //case CDATA_SECTION_NODE:
69474 //case COMMENT_NODE:
69477 //case DOCUMENT_NODE:
69478 //case DOCUMENT_TYPE_NODE:
69479 //cannot be imported.
69480 //case ENTITY_NODE:
69481 //case NOTATION_NODE:
69482 //can not hit in level3
69487 node2 = node.cloneNode(false); //false
69490 node2.ownerDocument = doc;
69491 node2.parentNode = null;
69494 var child = node.firstChild;
69497 node2.appendChild(_importNode(doc, child, deep));
69498 child = child.nextSibling;
69504 //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
69505 // attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
69508 function _cloneNode(doc, node, deep) {
69509 var node2 = new node.constructor();
69511 for (var n in node) {
69514 if (_typeof(v) != 'object') {
69515 if (v != node2[n]) {
69521 if (node.childNodes) {
69522 node2.childNodes = new NodeList();
69525 node2.ownerDocument = doc;
69527 switch (node2.nodeType) {
69529 var attrs = node.attributes;
69530 var attrs2 = node2.attributes = new NamedNodeMap();
69531 var len = attrs.length;
69532 attrs2._ownerElement = node2;
69534 for (var i = 0; i < len; i++) {
69535 node2.setAttributeNode(_cloneNode(doc, attrs.item(i), true));
69540 case ATTRIBUTE_NODE:
69545 var child = node.firstChild;
69548 node2.appendChild(_cloneNode(doc, child, deep));
69549 child = child.nextSibling;
69556 function __set__(object, key, value) {
69557 object[key] = value;
69562 if (Object.defineProperty) {
69563 var getTextContent = function getTextContent(node) {
69564 switch (node.nodeType) {
69566 case DOCUMENT_FRAGMENT_NODE:
69568 node = node.firstChild;
69571 if (node.nodeType !== 7 && node.nodeType !== 8) {
69572 buf.push(getTextContent(node));
69575 node = node.nextSibling;
69578 return buf.join('');
69581 return node.nodeValue;
69585 Object.defineProperty(LiveNodeList.prototype, 'length', {
69586 get: function get() {
69587 _updateLiveList(this);
69589 return this.$$length;
69592 Object.defineProperty(Node.prototype, 'textContent', {
69593 get: function get() {
69594 return getTextContent(this);
69596 set: function set(data) {
69597 switch (this.nodeType) {
69599 case DOCUMENT_FRAGMENT_NODE:
69600 while (this.firstChild) {
69601 this.removeChild(this.firstChild);
69604 if (data || String(data)) {
69605 this.appendChild(this.ownerDocument.createTextNode(data));
69614 this.nodeValue = data;
69619 __set__ = function __set__(object, key, value) {
69620 //console.log(value)
69621 object['$$' + key] = value;
69625 } //if(typeof require == 'function'){
69628 var DOMImplementation_1 = DOMImplementation;
69629 var XMLSerializer_1 = XMLSerializer$1; //}
69632 DOMImplementation: DOMImplementation_1,
69633 XMLSerializer: XMLSerializer_1
69636 var domParser = createCommonjsModule(function (module, exports) {
69637 function DOMParser(options) {
69638 this.options = options || {
69643 DOMParser.prototype.parseFromString = function (source, mimeType) {
69644 var options = this.options;
69645 var sax = new XMLReader();
69646 var domBuilder = options.domBuilder || new DOMHandler(); //contentHandler and LexicalHandler
69648 var errorHandler = options.errorHandler;
69649 var locator = options.locator;
69650 var defaultNSMap = options.xmlns || {};
69660 domBuilder.setDocumentLocator(locator);
69663 sax.errorHandler = buildErrorHandler(errorHandler, domBuilder, locator);
69664 sax.domBuilder = options.domBuilder || domBuilder;
69666 if (/\/x?html?$/.test(mimeType)) {
69667 entityMap.nbsp = '\xa0';
69668 entityMap.copy = '\xa9';
69669 defaultNSMap[''] = 'http://www.w3.org/1999/xhtml';
69672 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
69675 sax.parse(source, defaultNSMap, entityMap);
69677 sax.errorHandler.error("invalid doc source");
69680 return domBuilder.doc;
69683 function buildErrorHandler(errorImpl, domBuilder, locator) {
69685 if (domBuilder instanceof DOMHandler) {
69689 errorImpl = domBuilder;
69692 var errorHandler = {};
69693 var isCallback = errorImpl instanceof Function;
69694 locator = locator || {};
69696 function build(key) {
69697 var fn = errorImpl[key];
69699 if (!fn && isCallback) {
69700 fn = errorImpl.length == 2 ? function (msg) {
69701 errorImpl(key, msg);
69705 errorHandler[key] = fn && function (msg) {
69706 fn('[xmldom ' + key + ']\t' + msg + _locator(locator));
69707 } || function () {};
69712 build('fatalError');
69713 return errorHandler;
69714 } //console.log('#\n\n\n\n\n\n\n####')
69717 * +ContentHandler+ErrorHandler
69718 * +LexicalHandler+EntityResolver2
69719 * -DeclHandler-DTDHandler
69721 * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
69722 * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
69723 * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
69727 function DOMHandler() {
69728 this.cdata = false;
69731 function position(locator, node) {
69732 node.lineNumber = locator.lineNumber;
69733 node.columnNumber = locator.columnNumber;
69736 * @see org.xml.sax.ContentHandler#startDocument
69737 * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
69741 DOMHandler.prototype = {
69742 startDocument: function startDocument() {
69743 this.doc = new DOMImplementation().createDocument(null, null, null);
69745 if (this.locator) {
69746 this.doc.documentURI = this.locator.systemId;
69749 startElement: function startElement(namespaceURI, localName, qName, attrs) {
69750 var doc = this.doc;
69751 var el = doc.createElementNS(namespaceURI, qName || localName);
69752 var len = attrs.length;
69753 appendElement(this, el);
69754 this.currentElement = el;
69755 this.locator && position(this.locator, el);
69757 for (var i = 0; i < len; i++) {
69758 var namespaceURI = attrs.getURI(i);
69759 var value = attrs.getValue(i);
69760 var qName = attrs.getQName(i);
69761 var attr = doc.createAttributeNS(namespaceURI, qName);
69762 this.locator && position(attrs.getLocator(i), attr);
69763 attr.value = attr.nodeValue = value;
69764 el.setAttributeNode(attr);
69767 endElement: function endElement(namespaceURI, localName, qName) {
69768 var current = this.currentElement;
69769 var tagName = current.tagName;
69770 this.currentElement = current.parentNode;
69772 startPrefixMapping: function startPrefixMapping(prefix, uri) {},
69773 endPrefixMapping: function endPrefixMapping(prefix) {},
69774 processingInstruction: function processingInstruction(target, data) {
69775 var ins = this.doc.createProcessingInstruction(target, data);
69776 this.locator && position(this.locator, ins);
69777 appendElement(this, ins);
69779 ignorableWhitespace: function ignorableWhitespace(ch, start, length) {},
69780 characters: function characters(chars, start, length) {
69781 chars = _toString.apply(this, arguments); //console.log(chars)
69785 var charNode = this.doc.createCDATASection(chars);
69787 var charNode = this.doc.createTextNode(chars);
69790 if (this.currentElement) {
69791 this.currentElement.appendChild(charNode);
69792 } else if (/^\s*$/.test(chars)) {
69793 this.doc.appendChild(charNode); //process xml
69796 this.locator && position(this.locator, charNode);
69799 skippedEntity: function skippedEntity(name) {},
69800 endDocument: function endDocument() {
69801 this.doc.normalize();
69803 setDocumentLocator: function setDocumentLocator(locator) {
69804 if (this.locator = locator) {
69805 // && !('lineNumber' in locator)){
69806 locator.lineNumber = 0;
69810 comment: function comment(chars, start, length) {
69811 chars = _toString.apply(this, arguments);
69812 var comm = this.doc.createComment(chars);
69813 this.locator && position(this.locator, comm);
69814 appendElement(this, comm);
69816 startCDATA: function startCDATA() {
69817 //used in characters() methods
69820 endCDATA: function endCDATA() {
69821 this.cdata = false;
69823 startDTD: function startDTD(name, publicId, systemId) {
69824 var impl = this.doc.implementation;
69826 if (impl && impl.createDocumentType) {
69827 var dt = impl.createDocumentType(name, publicId, systemId);
69828 this.locator && position(this.locator, dt);
69829 appendElement(this, dt);
69834 * @see org.xml.sax.ErrorHandler
69835 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
69837 warning: function warning(error) {
69838 console.warn('[xmldom warning]\t' + error, _locator(this.locator));
69840 error: function error(_error) {
69841 console.error('[xmldom error]\t' + _error, _locator(this.locator));
69843 fatalError: function fatalError(error) {
69844 console.error('[xmldom fatalError]\t' + error, _locator(this.locator));
69849 function _locator(l) {
69851 return '\n@' + (l.systemId || '') + '#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']';
69855 function _toString(chars, start, length) {
69856 if (typeof chars == 'string') {
69857 return chars.substr(start, length);
69859 //java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
69860 if (chars.length >= start + length || start) {
69861 return new java.lang.String(chars, start, length) + '';
69868 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
69869 * used method of org.xml.sax.ext.LexicalHandler:
69870 * #comment(chars, start, length)
69873 * #startDTD(name, publicId, systemId)
69876 * IGNORED method of org.xml.sax.ext.LexicalHandler:
69878 * #startEntity(name)
69882 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
69883 * IGNORED method of org.xml.sax.ext.DeclHandler
69884 * #attributeDecl(eName, aName, type, mode, value)
69885 * #elementDecl(name, model)
69886 * #externalEntityDecl(name, publicId, systemId)
69887 * #internalEntityDecl(name, value)
69888 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
69889 * IGNORED method of org.xml.sax.EntityResolver2
69890 * #resolveEntity(String name,String publicId,String baseURI,String systemId)
69891 * #resolveEntity(publicId, systemId)
69892 * #getExternalSubset(name, baseURI)
69893 * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
69894 * IGNORED method of org.xml.sax.DTDHandler
69895 * #notationDecl(name, publicId, systemId) {};
69896 * #unparsedEntityDecl(name, publicId, systemId, notationName) {};
69900 "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g, function (key) {
69901 DOMHandler.prototype[key] = function () {
69905 /* 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 */
69907 function appendElement(hander, node) {
69908 if (!hander.currentElement) {
69909 hander.doc.appendChild(node);
69911 hander.currentElement.appendChild(node);
69913 } //appendChild and setAttributeNS are preformance key
69914 //if(typeof require == 'function'){
69917 var XMLReader = sax.XMLReader;
69918 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
69919 exports.XMLSerializer = dom.XMLSerializer;
69920 exports.DOMParser = DOMParser; //}
69923 var togeojson = createCommonjsModule(function (module, exports) {
69924 var toGeoJSON = function () {
69926 var removeSpace = /\s*/g,
69927 trimSpace = /^\s*|\s*$/g,
69928 splitSpace = /\s+/; // generate a short, numeric hash of a string
69930 function okhash(x) {
69931 if (!x || !x.length) return 0;
69933 for (var i = 0, h = 0; i < x.length; i++) {
69934 h = (h << 5) - h + x.charCodeAt(i) | 0;
69938 } // all Y children of X
69941 function get(x, y) {
69942 return x.getElementsByTagName(y);
69945 function attr(x, y) {
69946 return x.getAttribute(y);
69949 function attrf(x, y) {
69950 return parseFloat(attr(x, y));
69951 } // one Y child of X, if any, otherwise null
69954 function get1(x, y) {
69956 return n.length ? n[0] : null;
69957 } // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
69960 function norm(el) {
69961 if (el.normalize) {
69966 } // cast array x into numbers
69969 function numarray(x) {
69970 for (var j = 0, o = []; j < x.length; j++) {
69971 o[j] = parseFloat(x[j]);
69975 } // get the content of a text node, if any
69978 function nodeVal(x) {
69983 return x && x.textContent || '';
69984 } // get the contents of multiple text nodes, if present
69987 function getMulti(x, ys) {
69992 for (k = 0; k < ys.length; k++) {
69993 n = get1(x, ys[k]);
69994 if (n) o[ys[k]] = nodeVal(n);
69998 } // add properties of Y to X, overwriting if present in both
70001 function extend(x, y) {
70005 } // get one coordinate from a coordinate array, if any
70008 function coord1(v) {
70009 return numarray(v.replace(removeSpace, '').split(','));
70010 } // get all coordinates from a coordinate array as [[],[]]
70013 function coord(v) {
70014 var coords = v.replace(trimSpace, '').split(splitSpace),
70017 for (var i = 0; i < coords.length; i++) {
70018 o.push(coord1(coords[i]));
70024 function coordPair(x) {
70025 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
70026 ele = get1(x, 'ele'),
70027 // handle namespaced attribute in browser
70028 heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
70029 time = get1(x, 'time'),
70033 e = parseFloat(nodeVal(ele));
70042 time: time ? nodeVal(time) : null,
70043 heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
70045 } // create a new feature collection parent object
70050 type: 'FeatureCollection',
70057 if (typeof XMLSerializer !== 'undefined') {
70058 /* istanbul ignore next */
70059 serializer = new XMLSerializer(); // only require xmldom in a node environment
70060 } else if ( (typeof process === "undefined" ? "undefined" : _typeof(process)) === 'object' && !process.browser) {
70061 serializer = new domParser.XMLSerializer();
70064 function xml2str(str) {
70065 // IE9 will create a new XMLSerializer but it'll crash immediately.
70066 // This line is ignored because we don't run coverage tests in IE9
70068 /* istanbul ignore next */
70069 if (str.xml !== undefined) return str.xml;
70070 return serializer.serializeToString(str);
70074 kml: function kml(doc) {
70076 // styleindex keeps track of hashed styles in order to match features
70079 // stylemapindex keeps track of style maps to expose in properties
70080 styleMapIndex = {},
70081 // atomic geospatial types supported by KML - MultiGeometry is
70082 // handled separately
70083 geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
70084 // all root placemarks in the file
70085 placemarks = get(doc, 'Placemark'),
70086 styles = get(doc, 'Style'),
70087 styleMaps = get(doc, 'StyleMap');
70089 for (var k = 0; k < styles.length; k++) {
70090 var hash = okhash(xml2str(styles[k])).toString(16);
70091 styleIndex['#' + attr(styles[k], 'id')] = hash;
70092 styleByHash[hash] = styles[k];
70095 for (var l = 0; l < styleMaps.length; l++) {
70096 styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
70097 var pairs = get(styleMaps[l], 'Pair');
70100 for (var m = 0; m < pairs.length; m++) {
70101 pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
70104 styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
70107 for (var j = 0; j < placemarks.length; j++) {
70108 gj.features = gj.features.concat(getPlacemark(placemarks[j]));
70111 function kmlColor(v) {
70112 var color, opacity;
70115 if (v.substr(0, 1) === '#') {
70119 if (v.length === 6 || v.length === 3) {
70123 if (v.length === 8) {
70124 opacity = parseInt(v.substr(0, 2), 16) / 255;
70125 color = '#' + v.substr(6, 2) + v.substr(4, 2) + v.substr(2, 2);
70128 return [color, isNaN(opacity) ? undefined : opacity];
70131 function gxCoord(v) {
70132 return numarray(v.split(' '));
70135 function gxCoords(root) {
70136 var elems = get(root, 'coord'),
70139 if (elems.length === 0) elems = get(root, 'gx:coord');
70141 for (var i = 0; i < elems.length; i++) {
70142 coords.push(gxCoord(nodeVal(elems[i])));
70145 var timeElems = get(root, 'when');
70147 for (var j = 0; j < timeElems.length; j++) {
70148 times.push(nodeVal(timeElems[j]));
70157 function getGeometry(root) {
70166 if (get1(root, 'MultiGeometry')) {
70167 return getGeometry(get1(root, 'MultiGeometry'));
70170 if (get1(root, 'MultiTrack')) {
70171 return getGeometry(get1(root, 'MultiTrack'));
70174 if (get1(root, 'gx:MultiTrack')) {
70175 return getGeometry(get1(root, 'gx:MultiTrack'));
70178 for (i = 0; i < geotypes.length; i++) {
70179 geomNodes = get(root, geotypes[i]);
70182 for (j = 0; j < geomNodes.length; j++) {
70183 geomNode = geomNodes[j];
70185 if (geotypes[i] === 'Point') {
70188 coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
70190 } else if (geotypes[i] === 'LineString') {
70192 type: 'LineString',
70193 coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
70195 } else if (geotypes[i] === 'Polygon') {
70196 var rings = get(geomNode, 'LinearRing'),
70199 for (k = 0; k < rings.length; k++) {
70200 coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
70205 coordinates: coords
70207 } else if (geotypes[i] === 'Track' || geotypes[i] === 'gx:Track') {
70208 var track = gxCoords(geomNode);
70210 type: 'LineString',
70211 coordinates: track.coords
70213 if (track.times.length) coordTimes.push(track.times);
70221 coordTimes: coordTimes
70225 function getPlacemark(root) {
70226 var geomsAndTimes = getGeometry(root),
70229 name = nodeVal(get1(root, 'name')),
70230 address = nodeVal(get1(root, 'address')),
70231 styleUrl = nodeVal(get1(root, 'styleUrl')),
70232 description = nodeVal(get1(root, 'description')),
70233 timeSpan = get1(root, 'TimeSpan'),
70234 timeStamp = get1(root, 'TimeStamp'),
70235 extendedData = get1(root, 'ExtendedData'),
70236 lineStyle = get1(root, 'LineStyle'),
70237 polyStyle = get1(root, 'PolyStyle'),
70238 visibility = get1(root, 'visibility');
70239 if (!geomsAndTimes.geoms.length) return [];
70240 if (name) properties.name = name;
70241 if (address) properties.address = address;
70244 if (styleUrl[0] !== '#') {
70245 styleUrl = '#' + styleUrl;
70248 properties.styleUrl = styleUrl;
70250 if (styleIndex[styleUrl]) {
70251 properties.styleHash = styleIndex[styleUrl];
70254 if (styleMapIndex[styleUrl]) {
70255 properties.styleMapHash = styleMapIndex[styleUrl];
70256 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
70257 } // Try to populate the lineStyle or polyStyle since we got the style hash
70260 var style = styleByHash[properties.styleHash];
70263 if (!lineStyle) lineStyle = get1(style, 'LineStyle');
70264 if (!polyStyle) polyStyle = get1(style, 'PolyStyle');
70268 if (description) properties.description = description;
70271 var begin = nodeVal(get1(timeSpan, 'begin'));
70272 var end = nodeVal(get1(timeSpan, 'end'));
70273 properties.timespan = {
70280 properties.timestamp = nodeVal(get1(timeStamp, 'when'));
70284 var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
70285 color = linestyles[0],
70286 opacity = linestyles[1],
70287 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
70288 if (color) properties.stroke = color;
70289 if (!isNaN(opacity)) properties['stroke-opacity'] = opacity;
70290 if (!isNaN(width)) properties['stroke-width'] = width;
70294 var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
70295 pcolor = polystyles[0],
70296 popacity = polystyles[1],
70297 fill = nodeVal(get1(polyStyle, 'fill')),
70298 outline = nodeVal(get1(polyStyle, 'outline'));
70299 if (pcolor) properties.fill = pcolor;
70300 if (!isNaN(popacity)) properties['fill-opacity'] = popacity;
70301 if (fill) properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0;
70302 if (outline) properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0;
70305 if (extendedData) {
70306 var datas = get(extendedData, 'Data'),
70307 simpleDatas = get(extendedData, 'SimpleData');
70309 for (i = 0; i < datas.length; i++) {
70310 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
70313 for (i = 0; i < simpleDatas.length; i++) {
70314 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
70319 properties.visibility = nodeVal(visibility);
70322 if (geomsAndTimes.coordTimes.length) {
70323 properties.coordTimes = geomsAndTimes.coordTimes.length === 1 ? geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
70328 geometry: geomsAndTimes.geoms.length === 1 ? geomsAndTimes.geoms[0] : {
70329 type: 'GeometryCollection',
70330 geometries: geomsAndTimes.geoms
70332 properties: properties
70334 if (attr(root, 'id')) feature.id = attr(root, 'id');
70340 gpx: function gpx(doc) {
70342 tracks = get(doc, 'trk'),
70343 routes = get(doc, 'rte'),
70344 waypoints = get(doc, 'wpt'),
70345 // a feature collection
70349 for (i = 0; i < tracks.length; i++) {
70350 feature = getTrack(tracks[i]);
70351 if (feature) gj.features.push(feature);
70354 for (i = 0; i < routes.length; i++) {
70355 feature = getRoute(routes[i]);
70356 if (feature) gj.features.push(feature);
70359 for (i = 0; i < waypoints.length; i++) {
70360 gj.features.push(getPoint(waypoints[i]));
70363 function getPoints(node, pointname) {
70364 var pts = get(node, pointname),
70369 if (l < 2) return {}; // Invalid line in GeoJSON
70371 for (var i = 0; i < l; i++) {
70372 var c = coordPair(pts[i]);
70373 line.push(c.coordinates);
70374 if (c.time) times.push(c.time);
70375 if (c.heartRate) heartRates.push(c.heartRate);
70381 heartRates: heartRates
70385 function getTrack(node) {
70386 var segments = get(node, 'trkseg'),
70392 for (var i = 0; i < segments.length; i++) {
70393 line = getPoints(segments[i], 'trkpt');
70396 if (line.line) track.push(line.line);
70397 if (line.times && line.times.length) times.push(line.times);
70398 if (line.heartRates && line.heartRates.length) heartRates.push(line.heartRates);
70402 if (track.length === 0) return;
70403 var properties = getProperties(node);
70404 extend(properties, getLineStyle(get1(node, 'extensions')));
70405 if (times.length) properties.coordTimes = track.length === 1 ? times[0] : times;
70406 if (heartRates.length) properties.heartRates = track.length === 1 ? heartRates[0] : heartRates;
70409 properties: properties,
70411 type: track.length === 1 ? 'LineString' : 'MultiLineString',
70412 coordinates: track.length === 1 ? track[0] : track
70417 function getRoute(node) {
70418 var line = getPoints(node, 'rtept');
70419 if (!line.line) return;
70420 var prop = getProperties(node);
70421 extend(prop, getLineStyle(get1(node, 'extensions')));
70426 type: 'LineString',
70427 coordinates: line.line
70433 function getPoint(node) {
70434 var prop = getProperties(node);
70435 extend(prop, getMulti(node, ['sym']));
70441 coordinates: coordPair(node).coordinates
70446 function getLineStyle(extensions) {
70450 var lineStyle = get1(extensions, 'line');
70453 var color = nodeVal(get1(lineStyle, 'color')),
70454 opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
70455 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
70456 if (color) style.stroke = color;
70457 if (!isNaN(opacity)) style['stroke-opacity'] = opacity; // GPX width is in mm, convert to px with 96 px per inch
70459 if (!isNaN(width)) style['stroke-width'] = width * 96 / 25.4;
70466 function getProperties(node) {
70467 var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
70468 links = get(node, 'link');
70469 if (links.length) prop.links = [];
70471 for (var i = 0, link; i < links.length; i++) {
70473 href: attr(links[i], 'href')
70475 extend(link, getMulti(links[i], ['text', 'type']));
70476 prop.links.push(link);
70488 module.exports = toGeoJSON;
70491 var _initialized = false;
70492 var _enabled = false;
70496 function svgData(projection, context, dispatch) {
70497 var throttledRedraw = throttle(function () {
70498 dispatch.call('change');
70501 var _showLabels = true;
70502 var detected = utilDetect();
70503 var layer = select(null);
70514 if (_initialized) return; // run once
70519 function over(d3_event) {
70520 d3_event.stopPropagation();
70521 d3_event.preventDefault();
70522 d3_event.dataTransfer.dropEffect = 'copy';
70525 context.container().attr('dropzone', 'copy').on('drop.svgData', function (d3_event) {
70526 d3_event.stopPropagation();
70527 d3_event.preventDefault();
70528 if (!detected.filedrop) return;
70529 drawData.fileList(d3_event.dataTransfer.files);
70530 }).on('dragenter.svgData', over).on('dragexit.svgData', over).on('dragover.svgData', over);
70531 _initialized = true;
70534 function getService() {
70535 if (services.vectorTile && !_vtService) {
70536 _vtService = services.vectorTile;
70538 _vtService.event.on('loadedData', throttledRedraw);
70539 } else if (!services.vectorTile && _vtService) {
70546 function showLayer() {
70548 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
70549 dispatch.call('change');
70553 function hideLayer() {
70554 throttledRedraw.cancel();
70555 layer.transition().duration(250).style('opacity', 0).on('end', layerOff);
70558 function layerOn() {
70559 layer.style('display', 'block');
70562 function layerOff() {
70563 layer.selectAll('.viewfield-group').remove();
70564 layer.style('display', 'none');
70565 } // ensure that all geojson features in a collection have IDs
70568 function ensureIDs(gj) {
70569 if (!gj) return null;
70571 if (gj.type === 'FeatureCollection') {
70572 for (var i = 0; i < gj.features.length; i++) {
70573 ensureFeatureID(gj.features[i]);
70576 ensureFeatureID(gj);
70580 } // ensure that each single Feature object has a unique ID
70583 function ensureFeatureID(feature) {
70584 if (!feature) return;
70585 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
70587 } // Prefer an array of Features instead of a FeatureCollection
70590 function getFeatures(gj) {
70591 if (!gj) return [];
70593 if (gj.type === 'FeatureCollection') {
70594 return gj.features;
70600 function featureKey(d) {
70601 return d.__featurehash__;
70604 function isPolygon(d) {
70605 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
70608 function clipPathID(d) {
70609 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
70612 function featureClasses(d) {
70613 return ['data' + d.__featurehash__, d.geometry.type, isPolygon(d) ? 'area' : '', d.__layerID__ || ''].filter(Boolean).join(' ');
70616 function drawData(selection) {
70617 var vtService = getService();
70618 var getPath = svgPath(projection).geojson;
70619 var getAreaPath = svgPath(projection, null, true).geojson;
70620 var hasData = drawData.hasData();
70621 layer = selection.selectAll('.layer-mapdata').data(_enabled && hasData ? [0] : []);
70622 layer.exit().remove();
70623 layer = layer.enter().append('g').attr('class', 'layer-mapdata').merge(layer);
70624 var surface = context.surface();
70625 if (!surface || surface.empty()) return; // not ready to draw yet, starting up
70628 var geoData, polygonData;
70630 if (_template && vtService) {
70631 // fetch data from vector tile service
70632 var sourceID = _template;
70633 vtService.loadTiles(sourceID, _template, projection);
70634 geoData = vtService.data(sourceID, projection);
70636 geoData = getFeatures(_geojson);
70639 geoData = geoData.filter(getPath);
70640 polygonData = geoData.filter(isPolygon); // Draw clip paths for polygons
70642 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data').data(polygonData, featureKey);
70643 clipPaths.exit().remove();
70644 var clipPathsEnter = clipPaths.enter().append('clipPath').attr('class', 'clipPath-data').attr('id', clipPathID);
70645 clipPathsEnter.append('path');
70646 clipPaths.merge(clipPathsEnter).selectAll('path').attr('d', getAreaPath); // Draw fill, shadow, stroke layers
70648 var datagroups = layer.selectAll('g.datagroup').data(['fill', 'shadow', 'stroke']);
70649 datagroups = datagroups.enter().append('g').attr('class', function (d) {
70650 return 'datagroup datagroup-' + d;
70651 }).merge(datagroups); // Draw paths
70658 var paths = datagroups.selectAll('path').data(function (layer) {
70659 return pathData[layer];
70660 }, featureKey); // exit
70662 paths.exit().remove(); // enter/update
70664 paths = paths.enter().append('path').attr('class', function (d) {
70665 var datagroup = this.parentNode.__data__;
70666 return 'pathdata ' + datagroup + ' ' + featureClasses(d);
70667 }).attr('clip-path', function (d) {
70668 var datagroup = this.parentNode.__data__;
70669 return datagroup === 'fill' ? 'url(#' + clipPathID(d) + ')' : null;
70670 }).merge(paths).attr('d', function (d) {
70671 var datagroup = this.parentNode.__data__;
70672 return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
70675 layer.call(drawLabels, 'label-halo', geoData).call(drawLabels, 'label', geoData);
70677 function drawLabels(selection, textClass, data) {
70678 var labelPath = d3_geoPath(projection);
70679 var labelData = data.filter(function (d) {
70680 return _showLabels && d.properties && (d.properties.desc || d.properties.name);
70682 var labels = selection.selectAll('text.' + textClass).data(labelData, featureKey); // exit
70684 labels.exit().remove(); // enter/update
70686 labels = labels.enter().append('text').attr('class', function (d) {
70687 return textClass + ' ' + featureClasses(d);
70688 }).merge(labels).text(function (d) {
70689 return d.properties.desc || d.properties.name;
70690 }).attr('x', function (d) {
70691 var centroid = labelPath.centroid(d);
70692 return centroid[0] + 11;
70693 }).attr('y', function (d) {
70694 var centroid = labelPath.centroid(d);
70695 return centroid[1];
70700 function getExtension(fileName) {
70701 if (!fileName) return;
70702 var re = /\.(gpx|kml|(geo)?json)$/i;
70703 var match = fileName.toLowerCase().match(re);
70704 return match && match.length && match[0];
70707 function xmlToDom(textdata) {
70708 return new DOMParser().parseFromString(textdata, 'text/xml');
70711 drawData.setFile = function (extension, data) {
70718 switch (extension) {
70720 gj = togeojson.gpx(xmlToDom(data));
70724 gj = togeojson.kml(xmlToDom(data));
70729 gj = JSON.parse(data);
70735 if (Object.keys(gj).length) {
70736 _geojson = ensureIDs(gj);
70737 _src = extension + ' data file';
70741 dispatch.call('change');
70745 drawData.showLabels = function (val) {
70746 if (!arguments.length) return _showLabels;
70751 drawData.enabled = function (val) {
70752 if (!arguments.length) return _enabled;
70761 dispatch.call('change');
70765 drawData.hasData = function () {
70766 var gj = _geojson || {};
70767 return !!(_template || Object.keys(gj).length);
70770 drawData.template = function (val, src) {
70771 if (!arguments.length) return _template; // test source against OSM imagery blocklists..
70773 var osm = context.connection();
70776 var blocklists = osm.imageryBlocklists();
70781 for (var i = 0; i < blocklists.length; i++) {
70782 regex = blocklists[i];
70783 fail = regex.test(val);
70786 } // ensure at least one test was run.
70790 regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
70791 fail = regex.test(val);
70797 _geojson = null; // strip off the querystring/hash from the template,
70798 // it often includes the access token
70800 _src = src || 'vectortile:' + val.split(/[?#]/)[0];
70801 dispatch.call('change');
70805 drawData.geojson = function (gj, src) {
70806 if (!arguments.length) return _geojson;
70813 if (Object.keys(gj).length) {
70814 _geojson = ensureIDs(gj);
70815 _src = src || 'unknown.geojson';
70818 dispatch.call('change');
70822 drawData.fileList = function (fileList) {
70823 if (!arguments.length) return _fileList;
70825 _fileList = fileList;
70828 if (!fileList || !fileList.length) return this;
70829 var f = fileList[0];
70830 var extension = getExtension(f.name);
70831 var reader = new FileReader();
70833 reader.onload = function () {
70834 return function (e) {
70835 drawData.setFile(extension, e.target.result);
70839 reader.readAsText(f);
70843 drawData.url = function (url, defaultExtension) {
70847 _src = null; // strip off any querystring/hash from the url before checking extension
70849 var testUrl = url.split(/[?#]/)[0];
70850 var extension = getExtension(testUrl) || defaultExtension;
70854 d3_text(url).then(function (data) {
70855 drawData.setFile(extension, data);
70856 })["catch"](function () {
70860 drawData.template(url);
70866 drawData.getSrc = function () {
70870 drawData.fitZoom = function () {
70871 var features = getFeatures(_geojson);
70872 if (!features.length) return;
70873 var map = context.map();
70874 var viewport = map.trimmedExtent().polygon();
70875 var coords = features.reduce(function (coords, feature) {
70876 var geom = feature.geometry;
70877 if (!geom) return coords;
70878 var c = geom.coordinates;
70879 /* eslint-disable no-fallthrough */
70881 switch (geom.type) {
70889 case 'MultiPolygon':
70890 c = utilArrayFlatten(c);
70893 case 'MultiLineString':
70894 c = utilArrayFlatten(c);
70897 /* eslint-enable no-fallthrough */
70900 return utilArrayUnion(coords, c);
70903 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
70904 var extent = geoExtent(d3_geoBounds({
70905 type: 'LineString',
70906 coordinates: coords
70908 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
70918 function svgDebug(projection, context) {
70919 function drawDebug(selection) {
70920 var showTile = context.getDebug('tile');
70921 var showCollision = context.getDebug('collision');
70922 var showImagery = context.getDebug('imagery');
70923 var showTouchTargets = context.getDebug('target');
70924 var showDownloaded = context.getDebug('downloaded');
70925 var debugData = [];
70934 if (showCollision) {
70948 if (showTouchTargets) {
70951 label: 'touchTargets'
70955 if (showDownloaded) {
70958 label: 'downloaded'
70962 var legend = context.container().select('.main-content').selectAll('.debug-legend').data(debugData.length ? [0] : []);
70963 legend.exit().remove();
70964 legend = legend.enter().append('div').attr('class', 'fillD debug-legend').merge(legend);
70965 var legendItems = legend.selectAll('.debug-legend-item').data(debugData, function (d) {
70968 legendItems.exit().remove();
70969 legendItems.enter().append('span').attr('class', function (d) {
70970 return "debug-legend-item ".concat(d["class"]);
70971 }).text(function (d) {
70974 var layer = selection.selectAll('.layer-debug').data(showImagery || showDownloaded ? [0] : []);
70975 layer.exit().remove();
70976 layer = layer.enter().append('g').attr('class', 'layer-debug').merge(layer); // imagery
70978 var extent = context.map().extent();
70979 _mainFileFetcher.get('imagery').then(function (d) {
70980 var hits = showImagery && d.query.bbox(extent.rectangle(), true) || [];
70981 var features = hits.map(function (d) {
70982 return d.features[d.id];
70984 var imagery = layer.selectAll('path.debug-imagery').data(features);
70985 imagery.exit().remove();
70986 imagery.enter().append('path').attr('class', 'debug-imagery debug orange');
70987 })["catch"](function () {
70991 var osm = context.connection();
70992 var dataDownloaded = [];
70994 if (osm && showDownloaded) {
70995 var rtree = osm.caches('get').tile.rtree;
70996 dataDownloaded = rtree.all().map(function (bbox) {
71004 coordinates: [[[bbox.minX, bbox.minY], [bbox.minX, bbox.maxY], [bbox.maxX, bbox.maxY], [bbox.maxX, bbox.minY], [bbox.minX, bbox.minY]]]
71010 var downloaded = layer.selectAll('path.debug-downloaded').data(showDownloaded ? dataDownloaded : []);
71011 downloaded.exit().remove();
71012 downloaded.enter().append('path').attr('class', 'debug-downloaded debug purple'); // update
71014 layer.selectAll('path').attr('d', svgPath(projection).geojson);
71015 } // This looks strange because `enabled` methods on other layers are
71016 // chainable getter/setters, and this one is just a getter.
71019 drawDebug.enabled = function () {
71020 if (!arguments.length) {
71021 return context.getDebug('tile') || context.getDebug('collision') || context.getDebug('imagery') || context.getDebug('target') || context.getDebug('downloaded');
71031 A standalone SVG element that contains only a `defs` sub-element. To be
71032 used once globally, since defs IDs must be unique within a document.
71035 function svgDefs(context) {
71036 var _defsSelection = select(null);
71038 var _spritesheetIds = ['iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'community-sprite'];
71040 function drawDefs(selection) {
71041 _defsSelection = selection.append('defs'); // add markers
71043 _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
71044 // (they can't inherit it from the line they're attached to),
71045 // so we need to manually define markers for each color of tag
71046 // (also, it's slightly nicer if we can control the
71047 // positioning for different tags)
71050 function addSidedMarker(name, color, offset) {
71051 _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);
71054 addSidedMarker('natural', 'rgb(170, 170, 170)', 0); // for a coastline, the arrows are (somewhat unintuitively) on
71055 // the water side, so let's color them blue (with a gap) to
71056 // give a stronger indication
71058 addSidedMarker('coastline', '#77dede', 1);
71059 addSidedMarker('waterway', '#77dede', 1); // barriers have a dashed line, and separating the triangle
71060 // from the line visually suits that
71062 addSidedMarker('barrier', '#ddd', 1);
71063 addSidedMarker('man_made', '#fff', 0);
71065 _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');
71067 _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
71070 var patterns = _defsSelection.selectAll('pattern').data([// pattern name, pattern image name
71071 ['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) {
71072 return 'ideditor-pattern-' + d[0];
71073 }).attr('width', 32).attr('height', 32).attr('patternUnits', 'userSpaceOnUse');
71075 patterns.append('rect').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('class', function (d) {
71076 return 'pattern-color-' + d[0];
71078 patterns.append('image').attr('x', 0).attr('y', 0).attr('width', 32).attr('height', 32).attr('xlink:href', function (d) {
71079 return context.imagePath('pattern/' + d[1] + '.png');
71080 }); // add clip paths
71082 _defsSelection.selectAll('clipPath').data([12, 18, 20, 32, 45]).enter().append('clipPath').attr('id', function (d) {
71083 return 'ideditor-clip-square-' + d;
71084 }).append('rect').attr('x', 0).attr('y', 0).attr('width', function (d) {
71086 }).attr('height', function (d) {
71088 }); // add symbol spritesheets
71091 addSprites(_spritesheetIds, true);
71094 function addSprites(ids, overrideColors) {
71095 _spritesheetIds = utilArrayUniq(_spritesheetIds.concat(ids));
71097 var spritesheets = _defsSelection.selectAll('.spritesheet').data(_spritesheetIds);
71099 spritesheets.enter().append('g').attr('class', function (d) {
71100 return 'spritesheet spritesheet-' + d;
71101 }).each(function (d) {
71102 var url = context.imagePath(d + '.svg');
71103 var node = select(this).node();
71104 svg(url).then(function (svg) {
71105 node.appendChild(select(svg.documentElement).attr('id', 'ideditor-' + d).node());
71107 if (overrideColors && d !== 'iD-sprite') {
71108 // allow icon colors to be overridden..
71109 select(node).selectAll('path').attr('fill', 'currentColor');
71111 })["catch"](function () {
71115 spritesheets.exit().remove();
71118 drawDefs.addSprites = addSprites;
71122 var _layerEnabled = false;
71126 function svgKeepRight(projection, context, dispatch) {
71127 var throttledRedraw = throttle(function () {
71128 return dispatch.call('change');
71132 var touchLayer = select(null);
71133 var drawLayer = select(null);
71134 var layerVisible = false;
71136 function markerPath(selection, klass) {
71137 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');
71138 } // Loosely-coupled keepRight service for fetching issues.
71141 function getService() {
71142 if (services.keepRight && !_qaService) {
71143 _qaService = services.keepRight;
71145 _qaService.on('loaded', throttledRedraw);
71146 } else if (!services.keepRight && _qaService) {
71151 } // Show the markers
71154 function editOn() {
71155 if (!layerVisible) {
71156 layerVisible = true;
71157 drawLayer.style('display', 'block');
71159 } // Immediately remove the markers and their touch targets
71162 function editOff() {
71163 if (layerVisible) {
71164 layerVisible = false;
71165 drawLayer.style('display', 'none');
71166 drawLayer.selectAll('.qaItem.keepRight').remove();
71167 touchLayer.selectAll('.qaItem.keepRight').remove();
71169 } // Enable the layer. This shows the markers and transitions them to visible.
71172 function layerOn() {
71174 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
71175 return dispatch.call('change');
71177 } // Disable the layer. This transitions the layer invisible and then hides the markers.
71180 function layerOff() {
71181 throttledRedraw.cancel();
71182 drawLayer.interrupt();
71183 touchLayer.selectAll('.qaItem.keepRight').remove();
71184 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
71186 dispatch.call('change');
71188 } // Update the issue markers
71191 function updateMarkers() {
71192 if (!layerVisible || !_layerEnabled) return;
71193 var service = getService();
71194 var selectedID = context.selectedErrorID();
71195 var data = service ? service.getItems(projection) : [];
71196 var getTransform = svgPointTransform(projection); // Draw markers..
71198 var markers = drawLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
71202 markers.exit().remove(); // enter
71204 var markersEnter = markers.enter().append('g').attr('class', function (d) {
71205 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.parentIssueType);
71207 markersEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
71208 markersEnter.append('path').call(markerPath, 'shadow');
71209 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
71211 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
71212 return d.id === selectedID;
71213 }).attr('transform', getTransform); // Draw targets..
71215 if (touchLayer.empty()) return;
71216 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71217 var targets = touchLayer.selectAll('.qaItem.keepRight').data(data, function (d) {
71221 targets.exit().remove(); // enter/update
71223 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
71224 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
71225 }).attr('transform', getTransform);
71227 function sortY(a, b) {
71228 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];
71230 } // Draw the keepRight layer and schedule loading issues and updating markers.
71233 function drawKeepRight(selection) {
71234 var service = getService();
71235 var surface = context.surface();
71237 if (surface && !surface.empty()) {
71238 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71241 drawLayer = selection.selectAll('.layer-keepRight').data(service ? [0] : []);
71242 drawLayer.exit().remove();
71243 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-keepRight').style('display', _layerEnabled ? 'block' : 'none').merge(drawLayer);
71245 if (_layerEnabled) {
71246 if (service && ~~context.map().zoom() >= minZoom) {
71248 service.loadIssues(projection);
71254 } // Toggles the layer on and off
71257 drawKeepRight.enabled = function (val) {
71258 if (!arguments.length) return _layerEnabled;
71259 _layerEnabled = val;
71261 if (_layerEnabled) {
71266 if (context.selectedErrorID()) {
71267 context.enter(modeBrowse(context));
71271 dispatch.call('change');
71275 drawKeepRight.supported = function () {
71276 return !!getService();
71279 return drawKeepRight;
71282 function svgGeolocate(projection) {
71283 var layer = select(null);
71288 if (svgGeolocate.initialized) return; // run once
71290 svgGeolocate.enabled = false;
71291 svgGeolocate.initialized = true;
71294 function showLayer() {
71295 layer.style('display', 'block');
71298 function hideLayer() {
71299 layer.transition().duration(250).style('opacity', 0);
71302 function layerOn() {
71303 layer.style('opacity', 0).transition().duration(250).style('opacity', 1);
71306 function layerOff() {
71307 layer.style('display', 'none');
71310 function transform(d) {
71311 return svgPointTransform(projection)(d);
71314 function accuracy(accuracy, loc) {
71315 // converts accuracy to pixels...
71316 var degreesRadius = geoMetersToLat(accuracy),
71317 tangentLoc = [loc[0], loc[1] + degreesRadius],
71318 projectedTangent = projection(tangentLoc),
71319 projectedLoc = projection([loc[0], loc[1]]); // southern most point will have higher pixel value...
71321 return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
71324 function update() {
71325 var geolocation = {
71326 loc: [_position.coords.longitude, _position.coords.latitude]
71328 var groups = layer.selectAll('.geolocations').selectAll('.geolocation').data([geolocation]);
71329 groups.exit().remove();
71330 var pointsEnter = groups.enter().append('g').attr('class', 'geolocation');
71331 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');
71332 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');
71333 groups.merge(pointsEnter).attr('transform', transform);
71334 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
71337 function drawLocation(selection) {
71338 var enabled = svgGeolocate.enabled;
71339 layer = selection.selectAll('.layer-geolocate').data([0]);
71340 layer.exit().remove();
71341 var layerEnter = layer.enter().append('g').attr('class', 'layer-geolocate').style('display', enabled ? 'block' : 'none');
71342 layerEnter.append('g').attr('class', 'geolocations');
71343 layer = layerEnter.merge(layer);
71352 drawLocation.enabled = function (position, enabled) {
71353 if (!arguments.length) return svgGeolocate.enabled;
71354 _position = position;
71355 svgGeolocate.enabled = enabled;
71357 if (svgGeolocate.enabled) {
71368 return drawLocation;
71371 function svgLabels(projection, context) {
71372 var path = d3_geoPath(projection);
71373 var detected = utilDetect();
71374 var baselineHack = detected.ie || detected.browser.toLowerCase() === 'edge' || detected.browser.toLowerCase() === 'firefox' && detected.version >= 70;
71376 var _rdrawn = new RBush();
71378 var _rskipped = new RBush();
71380 var _textWidthCache = {};
71381 var _entitybboxes = {}; // Listed from highest to lowest priority
71383 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]];
71385 function shouldSkipIcon(preset) {
71386 var noIcons = ['building', 'landuse', 'natural'];
71387 return noIcons.some(function (s) {
71388 return preset.id.indexOf(s) >= 0;
71392 function get(array, prop) {
71393 return function (d, i) {
71394 return array[i][prop];
71398 function textWidth(text, size, elem) {
71399 var c = _textWidthCache[size];
71400 if (!c) c = _textWidthCache[size] = {};
71405 c[text] = elem.getComputedTextLength();
71408 var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
71410 if (str === null) {
71411 return size / 3 * 2 * text.length;
71413 return size / 3 * (2 * text.length + str.length);
71418 function drawLinePaths(selection, entities, filter, classes, labels) {
71419 var paths = selection.selectAll('path').filter(filter).data(entities, osmEntity.key); // exit
71421 paths.exit().remove(); // enter/update
71423 paths.enter().append('path').style('stroke-width', get(labels, 'font-size')).attr('id', function (d) {
71424 return 'ideditor-labelpath-' + d.id;
71425 }).attr('class', classes).merge(paths).attr('d', get(labels, 'lineString'));
71428 function drawLineLabels(selection, entities, filter, classes, labels) {
71429 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71431 texts.exit().remove(); // enter
71433 texts.enter().append('text').attr('class', function (d, i) {
71434 return classes + ' ' + labels[i].classes + ' ' + d.id;
71435 }).attr('dy', baselineHack ? '0.35em' : null).append('textPath').attr('class', 'textpath'); // update
71437 selection.selectAll('text.' + classes).selectAll('.textpath').filter(filter).data(entities, osmEntity.key).attr('startOffset', '50%').attr('xlink:href', function (d) {
71438 return '#ideditor-labelpath-' + d.id;
71439 }).text(utilDisplayNameForPath);
71442 function drawPointLabels(selection, entities, filter, classes, labels) {
71443 var texts = selection.selectAll('text.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71445 texts.exit().remove(); // enter/update
71447 texts.enter().append('text').attr('class', function (d, i) {
71448 return classes + ' ' + labels[i].classes + ' ' + d.id;
71449 }).merge(texts).attr('x', get(labels, 'x')).attr('y', get(labels, 'y')).style('text-anchor', get(labels, 'textAnchor')).text(utilDisplayName).each(function (d, i) {
71450 textWidth(utilDisplayName(d), labels[i].height, this);
71454 function drawAreaLabels(selection, entities, filter, classes, labels) {
71455 entities = entities.filter(hasText);
71456 labels = labels.filter(hasText);
71457 drawPointLabels(selection, entities, filter, classes, labels);
71459 function hasText(d, i) {
71460 return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
71464 function drawAreaIcons(selection, entities, filter, classes, labels) {
71465 var icons = selection.selectAll('use.' + classes).filter(filter).data(entities, osmEntity.key); // exit
71467 icons.exit().remove(); // enter/update
71469 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) {
71470 var preset = _mainPresetIndex.match(d, context.graph());
71471 var picon = preset && preset.icon;
71476 var isMaki = /^maki-/.test(picon);
71477 return '#' + picon + (isMaki ? '-15' : '');
71482 function drawCollisionBoxes(selection, rtree, which) {
71483 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
71486 if (context.getDebug('collision')) {
71487 gj = rtree.all().map(function (d) {
71490 coordinates: [[[d.minX, d.minY], [d.maxX, d.minY], [d.maxX, d.maxY], [d.minX, d.maxY], [d.minX, d.minY]]]
71495 var boxes = selection.selectAll('.' + which).data(gj); // exit
71497 boxes.exit().remove(); // enter/update
71499 boxes.enter().append('path').attr('class', classes).merge(boxes).attr('d', d3_geoPath());
71502 function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
71503 var wireframe = context.surface().classed('fill-wireframe');
71504 var zoom = geoScaleToZoom(projection.scale());
71505 var labelable = [];
71506 var renderNodeAs = {};
71507 var i, j, k, entity, geometry;
71509 for (i = 0; i < labelStack.length; i++) {
71510 labelable.push([]);
71518 _entitybboxes = {};
71520 for (i = 0; i < entities.length; i++) {
71521 entity = entities[i];
71522 var toRemove = [].concat(_entitybboxes[entity.id] || []).concat(_entitybboxes[entity.id + 'I'] || []);
71524 for (j = 0; j < toRemove.length; j++) {
71525 _rdrawn.remove(toRemove[j]);
71527 _rskipped.remove(toRemove[j]);
71530 } // Loop through all the entities to do some preprocessing
71533 for (i = 0; i < entities.length; i++) {
71534 entity = entities[i];
71535 geometry = entity.geometry(graph); // Insert collision boxes around interesting points/vertices
71537 if (geometry === 'point' || geometry === 'vertex' && isInterestingVertex(entity)) {
71538 var hasDirections = entity.directions(graph, projection).length;
71541 if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
71542 renderNodeAs[entity.id] = 'point';
71543 markerPadding = 20; // extra y for marker height
71545 renderNodeAs[entity.id] = 'vertex';
71549 var coord = projection(entity.loc);
71550 var nodePadding = 10;
71552 minX: coord[0] - nodePadding,
71553 minY: coord[1] - nodePadding - markerPadding,
71554 maxX: coord[0] + nodePadding,
71555 maxY: coord[1] + nodePadding
71557 doInsert(bbox, entity.id + 'P');
71558 } // From here on, treat vertices like points
71561 if (geometry === 'vertex') {
71562 geometry = 'point';
71563 } // Determine which entities are label-able
71566 var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
71567 var icon = preset && !shouldSkipIcon(preset) && preset.icon;
71568 if (!icon && !utilDisplayName(entity)) continue;
71570 for (k = 0; k < labelStack.length; k++) {
71571 var matchGeom = labelStack[k][0];
71572 var matchKey = labelStack[k][1];
71573 var matchVal = labelStack[k][2];
71574 var hasVal = entity.tags[matchKey];
71576 if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
71577 labelable[k].push(entity);
71592 }; // Try and find a valid label for labellable entities
71594 for (k = 0; k < labelable.length; k++) {
71595 var fontSize = labelStack[k][3];
71597 for (i = 0; i < labelable[k].length; i++) {
71598 entity = labelable[k][i];
71599 geometry = entity.geometry(graph);
71600 var getName = geometry === 'line' ? utilDisplayNameForPath : utilDisplayName;
71601 var name = getName(entity);
71602 var width = name && textWidth(name, fontSize);
71605 if (geometry === 'point' || geometry === 'vertex') {
71606 // no point or vertex labels in wireframe mode
71607 // no vertex labels at low zooms (vertices have no icons)
71608 if (wireframe) continue;
71609 var renderAs = renderNodeAs[entity.id];
71610 if (renderAs === 'vertex' && zoom < 17) continue;
71611 p = getPointLabel(entity, width, fontSize, renderAs);
71612 } else if (geometry === 'line') {
71613 p = getLineLabel(entity, width, fontSize);
71614 } else if (geometry === 'area') {
71615 p = getAreaLabel(entity, width, fontSize);
71619 if (geometry === 'vertex') {
71620 geometry = 'point';
71621 } // treat vertex like point
71624 p.classes = geometry + ' tag-' + labelStack[k][1];
71625 positions[geometry].push(p);
71626 labelled[geometry].push(entity);
71631 function isInterestingVertex(entity) {
71632 var selectedIDs = context.selectedIDs();
71633 return entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || selectedIDs.indexOf(entity.id) !== -1 || graph.parentWays(entity).some(function (parent) {
71634 return selectedIDs.indexOf(parent.id) !== -1;
71638 function getPointLabel(entity, width, height, geometry) {
71639 var y = geometry === 'point' ? -12 : 0;
71640 var pointOffsets = {
71641 ltr: [15, y, 'start'],
71642 rtl: [-15, y, 'end']
71644 var textDirection = _mainLocalizer.textDirection();
71645 var coord = projection(entity.loc);
71646 var textPadding = 2;
71647 var offset = pointOffsets[textDirection];
71651 x: coord[0] + offset[0],
71652 y: coord[1] + offset[1],
71653 textAnchor: offset[2]
71654 }; // insert a collision box for the text label..
71658 if (textDirection === 'rtl') {
71660 minX: p.x - width - textPadding,
71661 minY: p.y - height / 2 - textPadding,
71662 maxX: p.x + textPadding,
71663 maxY: p.y + height / 2 + textPadding
71667 minX: p.x - textPadding,
71668 minY: p.y - height / 2 - textPadding,
71669 maxX: p.x + width + textPadding,
71670 maxY: p.y + height / 2 + textPadding
71674 if (tryInsert([bbox], entity.id, true)) {
71679 function getLineLabel(entity, width, height) {
71680 var viewport = geoExtent(context.projection.clipExtent()).polygon();
71681 var points = graph.childNodes(entity).map(function (node) {
71682 return projection(node.loc);
71684 var length = geoPathLength(points);
71685 if (length < width + 20) return; // % along the line to attempt to place the label
71687 var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70, 25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
71690 for (var i = 0; i < lineOffsets.length; i++) {
71691 var offset = lineOffsets[i];
71692 var middle = offset / 100 * length;
71693 var start = middle - width / 2;
71694 if (start < 0 || start + width > length) continue; // generate subpath and ignore paths that are invalid or don't cross viewport.
71696 var sub = subpath(points, start, start + width);
71698 if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
71702 var isReverse = reverse(sub);
71705 sub = sub.reverse();
71709 var boxsize = (height + 2) / 2;
71711 for (var j = 0; j < sub.length - 1; j++) {
71713 var b = sub[j + 1]; // split up the text into small collision boxes
71715 var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
71717 for (var box = 0; box < num; box++) {
71718 var p = geoVecInterp(a, b, box / num);
71719 var x0 = p[0] - boxsize - padding;
71720 var y0 = p[1] - boxsize - padding;
71721 var x1 = p[0] + boxsize + padding;
71722 var y1 = p[1] + boxsize + padding;
71724 minX: Math.min(x0, x1),
71725 minY: Math.min(y0, y1),
71726 maxX: Math.max(x0, x1),
71727 maxY: Math.max(y0, y1)
71732 if (tryInsert(bboxes, entity.id, false)) {
71735 'font-size': height + 2,
71736 lineString: lineString(sub),
71737 startOffset: offset + '%'
71742 function reverse(p) {
71743 var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
71744 return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI / 2 && angle > -Math.PI / 2);
71747 function lineString(points) {
71748 return 'M' + points.join('L');
71751 function subpath(points, from, to) {
71753 var start, end, i0, i1;
71755 for (var i = 0; i < points.length - 1; i++) {
71757 var b = points[i + 1];
71758 var current = geoVecLength(a, b);
71761 if (!start && sofar + current >= from) {
71762 portion = (from - sofar) / current;
71763 start = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
71767 if (!end && sofar + current >= to) {
71768 portion = (to - sofar) / current;
71769 end = [a[0] + portion * (b[0] - a[0]), a[1] + portion * (b[1] - a[1])];
71776 var result = points.slice(i0, i1);
71777 result.unshift(start);
71783 function getAreaLabel(entity, width, height) {
71784 var centroid = path.centroid(entity.asGeoJSON(graph, true));
71785 var extent = entity.extent(graph);
71786 var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
71787 if (isNaN(centroid[0]) || areaWidth < 20) return;
71788 var preset = _mainPresetIndex.match(entity, context.graph());
71789 var picon = preset && preset.icon;
71795 // icon and label..
71797 addLabel(iconSize + padding);
71807 function addIcon() {
71808 var iconX = centroid[0] - iconSize / 2;
71809 var iconY = centroid[1] - iconSize / 2;
71813 maxX: iconX + iconSize,
71814 maxY: iconY + iconSize
71817 if (tryInsert([bbox], entity.id + 'I', true)) {
71818 p.transform = 'translate(' + iconX + ',' + iconY + ')';
71825 function addLabel(yOffset) {
71826 if (width && areaWidth >= width + 20) {
71827 var labelX = centroid[0];
71828 var labelY = centroid[1] + yOffset;
71830 minX: labelX - width / 2 - padding,
71831 minY: labelY - height / 2 - padding,
71832 maxX: labelX + width / 2 + padding,
71833 maxY: labelY + height / 2 + padding
71836 if (tryInsert([bbox], entity.id, true)) {
71839 p.textAnchor = 'middle';
71847 } // force insert a singular bounding box
71848 // singular box only, no array, id better be unique
71851 function doInsert(bbox, id) {
71853 var oldbox = _entitybboxes[id];
71856 _rdrawn.remove(oldbox);
71859 _entitybboxes[id] = bbox;
71861 _rdrawn.insert(bbox);
71864 function tryInsert(bboxes, id, saveSkipped) {
71865 var skipped = false;
71867 for (var i = 0; i < bboxes.length; i++) {
71868 var bbox = bboxes[i];
71869 bbox.id = id; // Check that label is visible
71871 if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
71876 if (_rdrawn.collides(bbox)) {
71882 _entitybboxes[id] = bboxes;
71886 _rskipped.load(bboxes);
71889 _rdrawn.load(bboxes);
71895 var layer = selection.selectAll('.layer-osm.labels');
71896 layer.selectAll('.labels-group').data(['halo', 'label', 'debug']).enter().append('g').attr('class', function (d) {
71897 return 'labels-group ' + d;
71899 var halo = layer.selectAll('.labels-group.halo');
71900 var label = layer.selectAll('.labels-group.label');
71901 var debug = layer.selectAll('.labels-group.debug'); // points
71903 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
71904 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point); // lines
71906 drawLinePaths(layer, labelled.line, filter, '', positions.line);
71907 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
71908 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line); // areas
71910 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
71911 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
71912 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
71913 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area); // debug
71915 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
71916 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
71917 layer.call(filterLabels);
71920 function filterLabels(selection) {
71921 var drawLayer = selection.selectAll('.layer-osm.labels');
71922 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
71923 layers.selectAll('.nolabel').classed('nolabel', false);
71924 var mouse = context.map().mouse();
71925 var graph = context.graph();
71926 var selectedIDs = context.selectedIDs();
71928 var pad, bbox; // hide labels near the mouse
71933 minX: mouse[0] - pad,
71934 minY: mouse[1] - pad,
71935 maxX: mouse[0] + pad,
71936 maxY: mouse[1] + pad
71939 var nearMouse = _rdrawn.search(bbox).map(function (entity) {
71943 ids.push.apply(ids, nearMouse);
71944 } // hide labels on selected nodes (they look weird when dragging / haloed)
71947 for (var i = 0; i < selectedIDs.length; i++) {
71948 var entity = graph.hasEntity(selectedIDs[i]);
71950 if (entity && entity.type === 'node') {
71951 ids.push(selectedIDs[i]);
71955 layers.selectAll(utilEntitySelector(ids)).classed('nolabel', true); // draw the mouse bbox if debugging is on..
71957 var debug = selection.selectAll('.labels-group.debug');
71960 if (context.getDebug('collision')) {
71963 coordinates: [[[bbox.minX, bbox.minY], [bbox.maxX, bbox.minY], [bbox.maxX, bbox.maxY], [bbox.minX, bbox.maxY], [bbox.minX, bbox.minY]]]
71967 var box = debug.selectAll('.debug-mouse').data(gj); // exit
71969 box.exit().remove(); // enter/update
71971 box.enter().append('path').attr('class', 'debug debug-mouse yellow').merge(box).attr('d', d3_geoPath());
71974 var throttleFilterLabels = throttle(filterLabels, 100);
71976 drawLabels.observe = function (selection) {
71977 var listener = function listener() {
71978 throttleFilterLabels(selection);
71981 selection.on('mousemove.hidelabels', listener);
71982 context.on('enter.hidelabels', listener);
71985 drawLabels.off = function (selection) {
71986 throttleFilterLabels.cancel();
71987 selection.on('mousemove.hidelabels', null);
71988 context.on('enter.hidelabels', null);
71994 var _layerEnabled$1 = false;
71998 function svgImproveOSM(projection, context, dispatch) {
71999 var throttledRedraw = throttle(function () {
72000 return dispatch.call('change');
72004 var touchLayer = select(null);
72005 var drawLayer = select(null);
72006 var layerVisible = false;
72008 function markerPath(selection, klass) {
72009 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');
72010 } // Loosely-coupled improveOSM service for fetching issues
72013 function getService() {
72014 if (services.improveOSM && !_qaService$1) {
72015 _qaService$1 = services.improveOSM;
72017 _qaService$1.on('loaded', throttledRedraw);
72018 } else if (!services.improveOSM && _qaService$1) {
72019 _qaService$1 = null;
72022 return _qaService$1;
72023 } // Show the markers
72026 function editOn() {
72027 if (!layerVisible) {
72028 layerVisible = true;
72029 drawLayer.style('display', 'block');
72031 } // Immediately remove the markers and their touch targets
72034 function editOff() {
72035 if (layerVisible) {
72036 layerVisible = false;
72037 drawLayer.style('display', 'none');
72038 drawLayer.selectAll('.qaItem.improveOSM').remove();
72039 touchLayer.selectAll('.qaItem.improveOSM').remove();
72041 } // Enable the layer. This shows the markers and transitions them to visible.
72044 function layerOn() {
72046 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
72047 return dispatch.call('change');
72049 } // Disable the layer. This transitions the layer invisible and then hides the markers.
72052 function layerOff() {
72053 throttledRedraw.cancel();
72054 drawLayer.interrupt();
72055 touchLayer.selectAll('.qaItem.improveOSM').remove();
72056 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
72058 dispatch.call('change');
72060 } // Update the issue markers
72063 function updateMarkers() {
72064 if (!layerVisible || !_layerEnabled$1) return;
72065 var service = getService();
72066 var selectedID = context.selectedErrorID();
72067 var data = service ? service.getItems(projection) : [];
72068 var getTransform = svgPointTransform(projection); // Draw markers..
72070 var markers = drawLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
72074 markers.exit().remove(); // enter
72076 var markersEnter = markers.enter().append('g').attr('class', function (d) {
72077 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
72079 markersEnter.append('polygon').call(markerPath, 'shadow');
72080 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
72081 markersEnter.append('polygon').attr('fill', 'currentColor').call(markerPath, 'qaItem-fill');
72082 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
72083 var picon = d.icon;
72088 var isMaki = /^maki-/.test(picon);
72089 return "#".concat(picon).concat(isMaki ? '-11' : '');
72093 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
72094 return d.id === selectedID;
72095 }).attr('transform', getTransform); // Draw targets..
72097 if (touchLayer.empty()) return;
72098 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72099 var targets = touchLayer.selectAll('.qaItem.improveOSM').data(data, function (d) {
72103 targets.exit().remove(); // enter/update
72105 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
72106 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
72107 }).attr('transform', getTransform);
72109 function sortY(a, b) {
72110 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
72112 } // Draw the ImproveOSM layer and schedule loading issues and updating markers.
72115 function drawImproveOSM(selection) {
72116 var service = getService();
72117 var surface = context.surface();
72119 if (surface && !surface.empty()) {
72120 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
72123 drawLayer = selection.selectAll('.layer-improveOSM').data(service ? [0] : []);
72124 drawLayer.exit().remove();
72125 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-improveOSM').style('display', _layerEnabled$1 ? 'block' : 'none').merge(drawLayer);
72127 if (_layerEnabled$1) {
72128 if (service && ~~context.map().zoom() >= minZoom) {
72130 service.loadIssues(projection);
72136 } // Toggles the layer on and off
72139 drawImproveOSM.enabled = function (val) {
72140 if (!arguments.length) return _layerEnabled$1;
72141 _layerEnabled$1 = val;
72143 if (_layerEnabled$1) {
72148 if (context.selectedErrorID()) {
72149 context.enter(modeBrowse(context));
72153 dispatch.call('change');
72157 drawImproveOSM.supported = function () {
72158 return !!getService();
72161 return drawImproveOSM;
72164 var _layerEnabled$2 = false;
72168 function svgOsmose(projection, context, dispatch) {
72169 var throttledRedraw = throttle(function () {
72170 return dispatch.call('change');
72174 var touchLayer = select(null);
72175 var drawLayer = select(null);
72176 var layerVisible = false;
72178 function markerPath(selection, klass) {
72179 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');
72180 } // Loosely-coupled osmose service for fetching issues
72183 function getService() {
72184 if (services.osmose && !_qaService$2) {
72185 _qaService$2 = services.osmose;
72187 _qaService$2.on('loaded', throttledRedraw);
72188 } else if (!services.osmose && _qaService$2) {
72189 _qaService$2 = null;
72192 return _qaService$2;
72193 } // Show the markers
72196 function editOn() {
72197 if (!layerVisible) {
72198 layerVisible = true;
72199 drawLayer.style('display', 'block');
72201 } // Immediately remove the markers and their touch targets
72204 function editOff() {
72205 if (layerVisible) {
72206 layerVisible = false;
72207 drawLayer.style('display', 'none');
72208 drawLayer.selectAll('.qaItem.osmose').remove();
72209 touchLayer.selectAll('.qaItem.osmose').remove();
72211 } // Enable the layer. This shows the markers and transitions them to visible.
72214 function layerOn() {
72216 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
72217 return dispatch.call('change');
72219 } // Disable the layer. This transitions the layer invisible and then hides the markers.
72222 function layerOff() {
72223 throttledRedraw.cancel();
72224 drawLayer.interrupt();
72225 touchLayer.selectAll('.qaItem.osmose').remove();
72226 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
72228 dispatch.call('change');
72230 } // Update the issue markers
72233 function updateMarkers() {
72234 if (!layerVisible || !_layerEnabled$2) return;
72235 var service = getService();
72236 var selectedID = context.selectedErrorID();
72237 var data = service ? service.getItems(projection) : [];
72238 var getTransform = svgPointTransform(projection); // Draw markers..
72240 var markers = drawLayer.selectAll('.qaItem.osmose').data(data, function (d) {
72244 markers.exit().remove(); // enter
72246 var markersEnter = markers.enter().append('g').attr('class', function (d) {
72247 return "qaItem ".concat(d.service, " itemId-").concat(d.id, " itemType-").concat(d.itemType);
72249 markersEnter.append('polygon').call(markerPath, 'shadow');
72250 markersEnter.append('ellipse').attr('cx', 0).attr('cy', 0).attr('rx', 4.5).attr('ry', 2).attr('class', 'stroke');
72251 markersEnter.append('polygon').attr('fill', function (d) {
72252 return service.getColor(d.item);
72253 }).call(markerPath, 'qaItem-fill');
72254 markersEnter.append('use').attr('transform', 'translate(-6.5, -23)').attr('class', 'icon-annotation').attr('width', '13px').attr('height', '13px').attr('xlink:href', function (d) {
72255 var picon = d.icon;
72260 var isMaki = /^maki-/.test(picon);
72261 return "#".concat(picon).concat(isMaki ? '-11' : '');
72265 markers.merge(markersEnter).sort(sortY).classed('selected', function (d) {
72266 return d.id === selectedID;
72267 }).attr('transform', getTransform); // Draw targets..
72269 if (touchLayer.empty()) return;
72270 var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
72271 var targets = touchLayer.selectAll('.qaItem.osmose').data(data, function (d) {
72275 targets.exit().remove(); // enter/update
72277 targets.enter().append('rect').attr('width', '20px').attr('height', '30px').attr('x', '-10px').attr('y', '-28px').merge(targets).sort(sortY).attr('class', function (d) {
72278 return "qaItem ".concat(d.service, " target ").concat(fillClass, " itemId-").concat(d.id);
72279 }).attr('transform', getTransform);
72281 function sortY(a, b) {
72282 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
72284 } // Draw the Osmose layer and schedule loading issues and updating markers.
72287 function drawOsmose(selection) {
72288 var service = getService();
72289 var surface = context.surface();
72291 if (surface && !surface.empty()) {
72292 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
72295 drawLayer = selection.selectAll('.layer-osmose').data(service ? [0] : []);
72296 drawLayer.exit().remove();
72297 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-osmose').style('display', _layerEnabled$2 ? 'block' : 'none').merge(drawLayer);
72299 if (_layerEnabled$2) {
72300 if (service && ~~context.map().zoom() >= minZoom) {
72302 service.loadIssues(projection);
72308 } // Toggles the layer on and off
72311 drawOsmose.enabled = function (val) {
72312 if (!arguments.length) return _layerEnabled$2;
72313 _layerEnabled$2 = val;
72315 if (_layerEnabled$2) {
72316 // Strings supplied by Osmose fetched before showing layer for first time
72317 // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
72318 // Also, If layer is toggled quickly multiple requests are sent
72319 getService().loadStrings().then(layerOn)["catch"](function (err) {
72320 console.log(err); // eslint-disable-line no-console
72325 if (context.selectedErrorID()) {
72326 context.enter(modeBrowse(context));
72330 dispatch.call('change');
72334 drawOsmose.supported = function () {
72335 return !!getService();
72341 function svgStreetside(projection, context, dispatch) {
72342 var throttledRedraw = throttle(function () {
72343 dispatch.call('change');
72347 var minMarkerZoom = 16;
72348 var minViewfieldZoom = 18;
72349 var layer = select(null);
72350 var _viewerYaw = 0;
72351 var _selectedSequence = null;
72360 if (svgStreetside.initialized) return; // run once
72362 svgStreetside.enabled = false;
72363 svgStreetside.initialized = true;
72370 function getService() {
72371 if (services.streetside && !_streetside) {
72372 _streetside = services.streetside;
72374 _streetside.event.on('viewerChanged.svgStreetside', viewerChanged).on('loadedImages.svgStreetside', throttledRedraw);
72375 } else if (!services.streetside && _streetside) {
72376 _streetside = null;
72379 return _streetside;
72386 function showLayer() {
72387 var service = getService();
72388 if (!service) return;
72390 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
72391 dispatch.call('change');
72399 function hideLayer() {
72400 throttledRedraw.cancel();
72401 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
72408 function editOn() {
72409 layer.style('display', 'block');
72416 function editOff() {
72417 layer.selectAll('.viewfield-group').remove();
72418 layer.style('display', 'none');
72421 * click() Handles 'bubble' point click event.
72425 function click(d3_event, d) {
72426 var service = getService();
72427 if (!service) return; // try to preserve the viewer rotation when staying on the same sequence
72429 if (d.sequenceKey !== _selectedSequence) {
72430 _viewerYaw = 0; // reset
72433 _selectedSequence = d.sequenceKey;
72434 service.ensureViewerLoaded(context).then(function () {
72435 service.selectImage(context, d.key).yaw(_viewerYaw).showViewer(context);
72437 context.map().centerEase(d.loc);
72444 function mouseover(d3_event, d) {
72445 var service = getService();
72446 if (service) service.setStyles(context, d);
72453 function mouseout() {
72454 var service = getService();
72455 if (service) service.setStyles(context, null);
72462 function transform(d) {
72463 var t = svgPointTransform(projection)(d);
72464 var rot = d.ca + _viewerYaw;
72467 t += ' rotate(' + Math.floor(rot) + ',0,0)';
72473 function viewerChanged() {
72474 var service = getService();
72475 if (!service) return;
72476 var viewer = service.viewer();
72477 if (!viewer) return; // update viewfield rotation
72479 _viewerYaw = viewer.getYaw(); // avoid updating if the map is currently transformed
72480 // e.g. during drags or easing.
72482 if (context.map().isTransformed()) return;
72483 layer.selectAll('.viewfield-group.currentView').attr('transform', transform);
72486 function filterBubbles(bubbles) {
72487 var fromDate = context.photos().fromDate();
72488 var toDate = context.photos().toDate();
72489 var usernames = context.photos().usernames();
72492 var fromTimestamp = new Date(fromDate).getTime();
72493 bubbles = bubbles.filter(function (bubble) {
72494 return new Date(bubble.captured_at).getTime() >= fromTimestamp;
72499 var toTimestamp = new Date(toDate).getTime();
72500 bubbles = bubbles.filter(function (bubble) {
72501 return new Date(bubble.captured_at).getTime() <= toTimestamp;
72506 bubbles = bubbles.filter(function (bubble) {
72507 return usernames.indexOf(bubble.captured_by) !== -1;
72514 function filterSequences(sequences) {
72515 var fromDate = context.photos().fromDate();
72516 var toDate = context.photos().toDate();
72517 var usernames = context.photos().usernames();
72520 var fromTimestamp = new Date(fromDate).getTime();
72521 sequences = sequences.filter(function (sequences) {
72522 return new Date(sequences.properties.captured_at).getTime() >= fromTimestamp;
72527 var toTimestamp = new Date(toDate).getTime();
72528 sequences = sequences.filter(function (sequences) {
72529 return new Date(sequences.properties.captured_at).getTime() <= toTimestamp;
72534 sequences = sequences.filter(function (sequences) {
72535 return usernames.indexOf(sequences.properties.captured_by) !== -1;
72546 function update() {
72547 var viewer = context.container().select('.photoviewer');
72548 var selected = viewer.empty() ? undefined : viewer.datum();
72549 var z = ~~context.map().zoom();
72550 var showMarkers = z >= minMarkerZoom;
72551 var showViewfields = z >= minViewfieldZoom;
72552 var service = getService();
72553 var sequences = [];
72556 if (context.photos().showsPanoramic()) {
72557 sequences = service ? service.sequences(projection) : [];
72558 bubbles = service && showMarkers ? service.bubbles(projection) : [];
72559 sequences = filterSequences(sequences);
72560 bubbles = filterBubbles(bubbles);
72563 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
72564 return d.properties.key;
72567 traces.exit().remove(); // enter/update
72569 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
72570 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(bubbles, function (d) {
72571 // force reenter once bubbles are attached to a sequence
72572 return d.key + (d.sequenceKey ? 'v1' : 'v0');
72575 groups.exit().remove(); // enter
72577 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
72578 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72580 var markers = groups.merge(groupsEnter).sort(function (a, b) {
72581 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1];
72582 }).attr('transform', transform).select('.viewfield-scale');
72583 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72584 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72585 viewfields.exit().remove(); // viewfields may or may not be drawn...
72586 // but if they are, draw below the circles
72588 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
72590 function viewfieldPath() {
72591 var d = this.parentNode.__data__;
72594 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
72596 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
72602 * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
72603 * 'svgStreetside()' is called from index.js
72607 function drawImages(selection) {
72608 var enabled = svgStreetside.enabled;
72609 var service = getService();
72610 layer = selection.selectAll('.layer-streetside-images').data(service ? [0] : []);
72611 layer.exit().remove();
72612 var layerEnter = layer.enter().append('g').attr('class', 'layer-streetside-images').style('display', enabled ? 'block' : 'none');
72613 layerEnter.append('g').attr('class', 'sequences');
72614 layerEnter.append('g').attr('class', 'markers');
72615 layer = layerEnter.merge(layer);
72618 if (service && ~~context.map().zoom() >= minZoom) {
72621 service.loadBubbles(projection);
72628 * drawImages.enabled().
72632 drawImages.enabled = function (_) {
72633 if (!arguments.length) return svgStreetside.enabled;
72634 svgStreetside.enabled = _;
72636 if (svgStreetside.enabled) {
72638 context.photos().on('change.streetside', update);
72641 context.photos().on('change.streetside', null);
72644 dispatch.call('change');
72648 * drawImages.supported().
72652 drawImages.supported = function () {
72653 return !!getService();
72660 function svgMapillaryImages(projection, context, dispatch) {
72661 var throttledRedraw = throttle(function () {
72662 dispatch.call('change');
72666 var minMarkerZoom = 16;
72667 var minViewfieldZoom = 18;
72668 var layer = select(null);
72672 var viewerCompassAngle;
72675 if (svgMapillaryImages.initialized) return; // run once
72677 svgMapillaryImages.enabled = false;
72678 svgMapillaryImages.initialized = true;
72681 function getService() {
72682 if (services.mapillary && !_mapillary) {
72683 _mapillary = services.mapillary;
72685 _mapillary.event.on('loadedImages', throttledRedraw);
72686 } else if (!services.mapillary && _mapillary) {
72693 function showLayer() {
72694 var service = getService();
72695 if (!service) return;
72697 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
72698 dispatch.call('change');
72702 function hideLayer() {
72703 throttledRedraw.cancel();
72704 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
72707 function editOn() {
72708 layer.style('display', 'block');
72711 function editOff() {
72712 layer.selectAll('.viewfield-group').remove();
72713 layer.style('display', 'none');
72716 function click(d3_event, d) {
72717 var service = getService();
72718 if (!service) return;
72719 service.ensureViewerLoaded(context).then(function () {
72720 service.selectImage(context, d.key).showViewer(context);
72722 context.map().centerEase(d.loc);
72725 function mouseover(d) {
72726 var service = getService();
72727 if (service) service.setStyles(context, d);
72730 function mouseout() {
72731 var service = getService();
72732 if (service) service.setStyles(context, null);
72735 function transform(d) {
72736 var t = svgPointTransform(projection)(d);
72738 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
72739 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
72741 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
72747 function filterImages(images) {
72748 var showsPano = context.photos().showsPanoramic();
72749 var showsFlat = context.photos().showsFlat();
72750 var fromDate = context.photos().fromDate();
72751 var toDate = context.photos().toDate();
72752 var usernames = context.photos().usernames();
72754 if (!showsPano || !showsFlat) {
72755 images = images.filter(function (image) {
72756 if (image.pano) return showsPano;
72762 var fromTimestamp = new Date(fromDate).getTime();
72763 images = images.filter(function (image) {
72764 return new Date(image.captured_at).getTime() >= fromTimestamp;
72769 var toTimestamp = new Date(toDate).getTime();
72770 images = images.filter(function (image) {
72771 return new Date(image.captured_at).getTime() <= toTimestamp;
72776 images = images.filter(function (image) {
72777 return usernames.indexOf(image.captured_by) !== -1;
72784 function filterSequences(sequences, service) {
72785 var showsPano = context.photos().showsPanoramic();
72786 var showsFlat = context.photos().showsFlat();
72787 var fromDate = context.photos().fromDate();
72788 var toDate = context.photos().toDate();
72789 var usernames = context.photos().usernames();
72791 if (!showsPano || !showsFlat) {
72792 sequences = sequences.filter(function (sequence) {
72793 if (sequence.properties.hasOwnProperty('pano')) {
72794 if (sequence.properties.pano) return showsPano;
72797 // if the sequence doesn't specify pano or not, search its images
72798 var cProps = sequence.properties.coordinateProperties;
72800 if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
72801 for (var index in cProps.image_keys) {
72802 var imageKey = cProps.image_keys[index];
72803 var image = service.cachedImage(imageKey);
72805 if (image && image.hasOwnProperty('pano')) {
72806 if (image.pano) return showsPano;
72818 var fromTimestamp = new Date(fromDate).getTime();
72819 sequences = sequences.filter(function (sequence) {
72820 return new Date(sequence.properties.captured_at).getTime() >= fromTimestamp;
72825 var toTimestamp = new Date(toDate).getTime();
72826 sequences = sequences.filter(function (sequence) {
72827 return new Date(sequence.properties.captured_at).getTime() <= toTimestamp;
72832 sequences = sequences.filter(function (sequence) {
72833 return usernames.indexOf(sequence.properties.username) !== -1;
72840 function update() {
72841 var z = ~~context.map().zoom();
72842 var showMarkers = z >= minMarkerZoom;
72843 var showViewfields = z >= minViewfieldZoom;
72844 var service = getService();
72845 var sequences = service ? service.sequences(projection) : [];
72846 var images = service && showMarkers ? service.images(projection) : [];
72847 images = filterImages(images);
72848 sequences = filterSequences(sequences, service);
72849 service.filterViewer(context);
72850 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
72851 return d.properties.key;
72854 traces.exit().remove(); // enter/update
72856 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
72857 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
72861 groups.exit().remove(); // enter
72863 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
72864 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
72866 var markers = groups.merge(groupsEnter).sort(function (a, b) {
72867 return b.loc[1] - a.loc[1]; // sort Y
72868 }).attr('transform', transform).select('.viewfield-scale');
72869 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
72870 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
72871 viewfields.exit().remove();
72872 viewfields.enter() // viewfields may or may not be drawn...
72873 .insert('path', 'circle') // but if they are, draw below the circles
72874 .attr('class', 'viewfield').classed('pano', function () {
72875 return this.parentNode.__data__.pano;
72876 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
72878 function viewfieldPath() {
72879 var d = this.parentNode.__data__;
72882 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
72884 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
72889 function drawImages(selection) {
72890 var enabled = svgMapillaryImages.enabled;
72891 var service = getService();
72892 layer = selection.selectAll('.layer-mapillary').data(service ? [0] : []);
72893 layer.exit().remove();
72894 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary').style('display', enabled ? 'block' : 'none');
72895 layerEnter.append('g').attr('class', 'sequences');
72896 layerEnter.append('g').attr('class', 'markers');
72897 layer = layerEnter.merge(layer);
72900 if (service && ~~context.map().zoom() >= minZoom) {
72903 service.loadImages(projection);
72910 drawImages.enabled = function (_) {
72911 if (!arguments.length) return svgMapillaryImages.enabled;
72912 svgMapillaryImages.enabled = _;
72914 if (svgMapillaryImages.enabled) {
72916 context.photos().on('change.mapillary_images', update);
72919 context.photos().on('change.mapillary_images', null);
72922 dispatch.call('change');
72926 drawImages.supported = function () {
72927 return !!getService();
72934 function svgMapillaryPosition(projection, context) {
72935 var throttledRedraw = throttle(function () {
72940 var minViewfieldZoom = 18;
72941 var layer = select(null);
72945 var viewerCompassAngle;
72948 if (svgMapillaryPosition.initialized) return; // run once
72950 svgMapillaryPosition.initialized = true;
72953 function getService() {
72954 if (services.mapillary && !_mapillary) {
72955 _mapillary = services.mapillary;
72957 _mapillary.event.on('nodeChanged', throttledRedraw);
72959 _mapillary.event.on('bearingChanged', function (e) {
72960 viewerCompassAngle = e;
72961 if (context.map().isTransformed()) return;
72962 layer.selectAll('.viewfield-group.currentView').filter(function (d) {
72964 }).attr('transform', transform);
72966 } else if (!services.mapillary && _mapillary) {
72973 function editOn() {
72974 layer.style('display', 'block');
72977 function editOff() {
72978 layer.selectAll('.viewfield-group').remove();
72979 layer.style('display', 'none');
72982 function transform(d) {
72983 var t = svgPointTransform(projection)(d);
72985 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
72986 t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
72988 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
72994 function update() {
72995 var z = ~~context.map().zoom();
72996 var showViewfields = z >= minViewfieldZoom;
72997 var service = getService();
72998 var node = service && service.getActiveImage();
72999 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(node ? [node] : [], function (d) {
73003 groups.exit().remove(); // enter
73005 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group currentView highlighted');
73006 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
73008 var markers = groups.merge(groupsEnter).attr('transform', transform).select('.viewfield-scale');
73009 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
73010 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
73011 viewfields.exit().remove();
73012 viewfields.enter().insert('path', 'circle').attr('class', 'viewfield').classed('pano', function () {
73013 return this.parentNode.__data__.pano;
73014 }).attr('transform', 'scale(1.5,1.5),translate(-8, -13)').attr('d', viewfieldPath);
73016 function viewfieldPath() {
73017 var d = this.parentNode.__data__;
73020 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
73022 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
73027 function drawImages(selection) {
73028 var service = getService();
73029 layer = selection.selectAll('.layer-mapillary-position').data(service ? [0] : []);
73030 layer.exit().remove();
73031 var layerEnter = layer.enter().append('g').attr('class', 'layer-mapillary-position');
73032 layerEnter.append('g').attr('class', 'markers');
73033 layer = layerEnter.merge(layer);
73035 if (service && ~~context.map().zoom() >= minZoom) {
73043 drawImages.enabled = function () {
73048 drawImages.supported = function () {
73049 return !!getService();
73056 function svgMapillarySigns(projection, context, dispatch) {
73057 var throttledRedraw = throttle(function () {
73058 dispatch.call('change');
73062 var layer = select(null);
73067 if (svgMapillarySigns.initialized) return; // run once
73069 svgMapillarySigns.enabled = false;
73070 svgMapillarySigns.initialized = true;
73073 function getService() {
73074 if (services.mapillary && !_mapillary) {
73075 _mapillary = services.mapillary;
73077 _mapillary.event.on('loadedSigns', throttledRedraw);
73078 } else if (!services.mapillary && _mapillary) {
73085 function showLayer() {
73086 var service = getService();
73087 if (!service) return;
73088 service.loadSignResources(context);
73092 function hideLayer() {
73093 throttledRedraw.cancel();
73097 function editOn() {
73098 layer.style('display', 'block');
73101 function editOff() {
73102 layer.selectAll('.icon-sign').remove();
73103 layer.style('display', 'none');
73106 function click(d3_event, d) {
73107 var service = getService();
73108 if (!service) return;
73109 context.map().centerEase(d.loc);
73110 var selectedImageKey = service.getSelectedImageKey();
73112 var highlightedDetection; // Pick one of the images the sign was detected in,
73113 // preference given to an image already selected.
73115 d.detections.forEach(function (detection) {
73116 if (!imageKey || selectedImageKey === detection.image_key) {
73117 imageKey = detection.image_key;
73118 highlightedDetection = detection;
73122 if (imageKey === selectedImageKey) {
73123 service.highlightDetection(highlightedDetection).selectImage(context, imageKey);
73125 service.ensureViewerLoaded(context).then(function () {
73126 service.highlightDetection(highlightedDetection).selectImage(context, imageKey).showViewer(context);
73131 function filterData(detectedFeatures) {
73132 var service = getService();
73133 var fromDate = context.photos().fromDate();
73134 var toDate = context.photos().toDate();
73135 var usernames = context.photos().usernames();
73138 var fromTimestamp = new Date(fromDate).getTime();
73139 detectedFeatures = detectedFeatures.filter(function (feature) {
73140 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
73145 var toTimestamp = new Date(toDate).getTime();
73146 detectedFeatures = detectedFeatures.filter(function (feature) {
73147 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
73151 if (usernames && service) {
73152 detectedFeatures = detectedFeatures.filter(function (feature) {
73153 return feature.detections.some(function (detection) {
73154 var imageKey = detection.image_key;
73155 var image = service.cachedImage(imageKey);
73156 return image && usernames.indexOf(image.captured_by) !== -1;
73161 return detectedFeatures;
73164 function update() {
73165 var service = getService();
73166 var data = service ? service.signs(projection) : [];
73167 data = filterData(data);
73168 var selectedImageKey = service.getSelectedImageKey();
73169 var transform = svgPointTransform(projection);
73170 var signs = layer.selectAll('.icon-sign').data(data, function (d) {
73174 signs.exit().remove(); // enter
73176 var enter = signs.enter().append('g').attr('class', 'icon-sign icon-detected').on('click', click);
73177 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
73178 return '#' + d.value;
73180 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
73182 signs.merge(enter).attr('transform', transform).classed('currentView', function (d) {
73183 return d.detections.some(function (detection) {
73184 return detection.image_key === selectedImageKey;
73186 }).sort(function (a, b) {
73187 var aSelected = a.detections.some(function (detection) {
73188 return detection.image_key === selectedImageKey;
73190 var bSelected = b.detections.some(function (detection) {
73191 return detection.image_key === selectedImageKey;
73194 if (aSelected === bSelected) {
73195 return b.loc[1] - a.loc[1]; // sort Y
73196 } else if (aSelected) {
73204 function drawSigns(selection) {
73205 var enabled = svgMapillarySigns.enabled;
73206 var service = getService();
73207 layer = selection.selectAll('.layer-mapillary-signs').data(service ? [0] : []);
73208 layer.exit().remove();
73209 layer = layer.enter().append('g').attr('class', 'layer-mapillary-signs layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
73212 if (service && ~~context.map().zoom() >= minZoom) {
73215 service.loadSigns(projection);
73216 service.showSignDetections(true);
73220 } else if (service) {
73221 service.showSignDetections(false);
73225 drawSigns.enabled = function (_) {
73226 if (!arguments.length) return svgMapillarySigns.enabled;
73227 svgMapillarySigns.enabled = _;
73229 if (svgMapillarySigns.enabled) {
73231 context.photos().on('change.mapillary_signs', update);
73234 context.photos().on('change.mapillary_signs', null);
73237 dispatch.call('change');
73241 drawSigns.supported = function () {
73242 return !!getService();
73249 function svgMapillaryMapFeatures(projection, context, dispatch) {
73250 var throttledRedraw = throttle(function () {
73251 dispatch.call('change');
73255 var layer = select(null);
73260 if (svgMapillaryMapFeatures.initialized) return; // run once
73262 svgMapillaryMapFeatures.enabled = false;
73263 svgMapillaryMapFeatures.initialized = true;
73266 function getService() {
73267 if (services.mapillary && !_mapillary) {
73268 _mapillary = services.mapillary;
73270 _mapillary.event.on('loadedMapFeatures', throttledRedraw);
73271 } else if (!services.mapillary && _mapillary) {
73278 function showLayer() {
73279 var service = getService();
73280 if (!service) return;
73281 service.loadObjectResources(context);
73285 function hideLayer() {
73286 throttledRedraw.cancel();
73290 function editOn() {
73291 layer.style('display', 'block');
73294 function editOff() {
73295 layer.selectAll('.icon-map-feature').remove();
73296 layer.style('display', 'none');
73299 function click(d3_event, d) {
73300 var service = getService();
73301 if (!service) return;
73302 context.map().centerEase(d.loc);
73303 var selectedImageKey = service.getSelectedImageKey();
73305 var highlightedDetection; // Pick one of the images the map feature was detected in,
73306 // preference given to an image already selected.
73308 d.detections.forEach(function (detection) {
73309 if (!imageKey || selectedImageKey === detection.image_key) {
73310 imageKey = detection.image_key;
73311 highlightedDetection = detection;
73315 if (imageKey === selectedImageKey) {
73316 service.highlightDetection(highlightedDetection).selectImage(context, imageKey);
73318 service.ensureViewerLoaded(context).then(function () {
73319 service.highlightDetection(highlightedDetection).selectImage(context, imageKey).showViewer(context);
73324 function filterData(detectedFeatures) {
73325 var service = getService();
73326 var fromDate = context.photos().fromDate();
73327 var toDate = context.photos().toDate();
73328 var usernames = context.photos().usernames();
73331 var fromTimestamp = new Date(fromDate).getTime();
73332 detectedFeatures = detectedFeatures.filter(function (feature) {
73333 return new Date(feature.last_seen_at).getTime() >= fromTimestamp;
73338 var toTimestamp = new Date(toDate).getTime();
73339 detectedFeatures = detectedFeatures.filter(function (feature) {
73340 return new Date(feature.first_seen_at).getTime() <= toTimestamp;
73344 if (usernames && service) {
73345 detectedFeatures = detectedFeatures.filter(function (feature) {
73346 return feature.detections.some(function (detection) {
73347 var imageKey = detection.image_key;
73348 var image = service.cachedImage(imageKey);
73349 return image && usernames.indexOf(image.captured_by) !== -1;
73354 return detectedFeatures;
73357 function update() {
73358 var service = getService();
73359 var data = service ? service.mapFeatures(projection) : [];
73360 data = filterData(data);
73361 var selectedImageKey = service && service.getSelectedImageKey();
73362 var transform = svgPointTransform(projection);
73363 var mapFeatures = layer.selectAll('.icon-map-feature').data(data, function (d) {
73367 mapFeatures.exit().remove(); // enter
73369 var enter = mapFeatures.enter().append('g').attr('class', 'icon-map-feature icon-detected').on('click', click);
73370 enter.append('title').text(function (d) {
73371 var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
73372 return _t('mapillary_map_features.' + id);
73374 enter.append('use').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px').attr('xlink:href', function (d) {
73375 if (d.value === 'object--billboard') {
73376 // no billboard icon right now, so use the advertisement icon
73377 return '#object--sign--advertisement';
73380 return '#' + d.value;
73382 enter.append('rect').attr('width', '24px').attr('height', '24px').attr('x', '-12px').attr('y', '-12px'); // update
73384 mapFeatures.merge(enter).attr('transform', transform).classed('currentView', function (d) {
73385 return d.detections.some(function (detection) {
73386 return detection.image_key === selectedImageKey;
73388 }).sort(function (a, b) {
73389 var aSelected = a.detections.some(function (detection) {
73390 return detection.image_key === selectedImageKey;
73392 var bSelected = b.detections.some(function (detection) {
73393 return detection.image_key === selectedImageKey;
73396 if (aSelected === bSelected) {
73397 return b.loc[1] - a.loc[1]; // sort Y
73398 } else if (aSelected) {
73406 function drawMapFeatures(selection) {
73407 var enabled = svgMapillaryMapFeatures.enabled;
73408 var service = getService();
73409 layer = selection.selectAll('.layer-mapillary-map-features').data(service ? [0] : []);
73410 layer.exit().remove();
73411 layer = layer.enter().append('g').attr('class', 'layer-mapillary-map-features layer-mapillary-detections').style('display', enabled ? 'block' : 'none').merge(layer);
73414 if (service && ~~context.map().zoom() >= minZoom) {
73417 service.loadMapFeatures(projection);
73418 service.showFeatureDetections(true);
73422 } else if (service) {
73423 service.showFeatureDetections(false);
73427 drawMapFeatures.enabled = function (_) {
73428 if (!arguments.length) return svgMapillaryMapFeatures.enabled;
73429 svgMapillaryMapFeatures.enabled = _;
73431 if (svgMapillaryMapFeatures.enabled) {
73433 context.photos().on('change.mapillary_map_features', update);
73436 context.photos().on('change.mapillary_map_features', null);
73439 dispatch.call('change');
73443 drawMapFeatures.supported = function () {
73444 return !!getService();
73448 return drawMapFeatures;
73451 function svgOpenstreetcamImages(projection, context, dispatch) {
73452 var throttledRedraw = throttle(function () {
73453 dispatch.call('change');
73457 var minMarkerZoom = 16;
73458 var minViewfieldZoom = 18;
73459 var layer = select(null);
73461 var _openstreetcam;
73464 if (svgOpenstreetcamImages.initialized) return; // run once
73466 svgOpenstreetcamImages.enabled = false;
73467 svgOpenstreetcamImages.initialized = true;
73470 function getService() {
73471 if (services.openstreetcam && !_openstreetcam) {
73472 _openstreetcam = services.openstreetcam;
73474 _openstreetcam.event.on('loadedImages', throttledRedraw);
73475 } else if (!services.openstreetcam && _openstreetcam) {
73476 _openstreetcam = null;
73479 return _openstreetcam;
73482 function showLayer() {
73483 var service = getService();
73484 if (!service) return;
73486 layer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end', function () {
73487 dispatch.call('change');
73491 function hideLayer() {
73492 throttledRedraw.cancel();
73493 layer.transition().duration(250).style('opacity', 0).on('end', editOff);
73496 function editOn() {
73497 layer.style('display', 'block');
73500 function editOff() {
73501 layer.selectAll('.viewfield-group').remove();
73502 layer.style('display', 'none');
73505 function click(d3_event, d) {
73506 var service = getService();
73507 if (!service) return;
73508 service.ensureViewerLoaded(context).then(function () {
73509 service.selectImage(context, d.key).showViewer(context);
73511 context.map().centerEase(d.loc);
73514 function mouseover(d3_event, d) {
73515 var service = getService();
73516 if (service) service.setStyles(context, d);
73519 function mouseout() {
73520 var service = getService();
73521 if (service) service.setStyles(context, null);
73524 function transform(d) {
73525 var t = svgPointTransform(projection)(d);
73528 t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
73534 function filterImages(images) {
73535 var fromDate = context.photos().fromDate();
73536 var toDate = context.photos().toDate();
73537 var usernames = context.photos().usernames();
73540 var fromTimestamp = new Date(fromDate).getTime();
73541 images = images.filter(function (item) {
73542 return new Date(item.captured_at).getTime() >= fromTimestamp;
73547 var toTimestamp = new Date(toDate).getTime();
73548 images = images.filter(function (item) {
73549 return new Date(item.captured_at).getTime() <= toTimestamp;
73554 images = images.filter(function (item) {
73555 return usernames.indexOf(item.captured_by) !== -1;
73562 function filterSequences(sequences) {
73563 var fromDate = context.photos().fromDate();
73564 var toDate = context.photos().toDate();
73565 var usernames = context.photos().usernames();
73568 var fromTimestamp = new Date(fromDate).getTime();
73569 sequences = sequences.filter(function (image) {
73570 return new Date(image.properties.captured_at).getTime() >= fromTimestamp;
73575 var toTimestamp = new Date(toDate).getTime();
73576 sequences = sequences.filter(function (image) {
73577 return new Date(image.properties.captured_at).getTime() <= toTimestamp;
73582 sequences = sequences.filter(function (image) {
73583 return usernames.indexOf(image.properties.captured_by) !== -1;
73590 function update() {
73591 var viewer = context.container().select('.photoviewer');
73592 var selected = viewer.empty() ? undefined : viewer.datum();
73593 var z = ~~context.map().zoom();
73594 var showMarkers = z >= minMarkerZoom;
73595 var showViewfields = z >= minViewfieldZoom;
73596 var service = getService();
73597 var sequences = [];
73600 if (context.photos().showsFlat()) {
73601 sequences = service ? service.sequences(projection) : [];
73602 images = service && showMarkers ? service.images(projection) : [];
73603 sequences = filterSequences(sequences);
73604 images = filterImages(images);
73607 var traces = layer.selectAll('.sequences').selectAll('.sequence').data(sequences, function (d) {
73608 return d.properties.key;
73611 traces.exit().remove(); // enter/update
73613 traces = traces.enter().append('path').attr('class', 'sequence').merge(traces).attr('d', svgPath(projection).geojson);
73614 var groups = layer.selectAll('.markers').selectAll('.viewfield-group').data(images, function (d) {
73618 groups.exit().remove(); // enter
73620 var groupsEnter = groups.enter().append('g').attr('class', 'viewfield-group').on('mouseenter', mouseover).on('mouseleave', mouseout).on('click', click);
73621 groupsEnter.append('g').attr('class', 'viewfield-scale'); // update
73623 var markers = groups.merge(groupsEnter).sort(function (a, b) {
73624 return a === selected ? 1 : b === selected ? -1 : b.loc[1] - a.loc[1]; // sort Y
73625 }).attr('transform', transform).select('.viewfield-scale');
73626 markers.selectAll('circle').data([0]).enter().append('circle').attr('dx', '0').attr('dy', '0').attr('r', '6');
73627 var viewfields = markers.selectAll('.viewfield').data(showViewfields ? [0] : []);
73628 viewfields.exit().remove();
73629 viewfields.enter() // viewfields may or may not be drawn...
73630 .insert('path', 'circle') // but if they are, draw below the circles
73631 .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');
73634 function drawImages(selection) {
73635 var enabled = svgOpenstreetcamImages.enabled,
73636 service = getService();
73637 layer = selection.selectAll('.layer-openstreetcam').data(service ? [0] : []);
73638 layer.exit().remove();
73639 var layerEnter = layer.enter().append('g').attr('class', 'layer-openstreetcam').style('display', enabled ? 'block' : 'none');
73640 layerEnter.append('g').attr('class', 'sequences');
73641 layerEnter.append('g').attr('class', 'markers');
73642 layer = layerEnter.merge(layer);
73645 if (service && ~~context.map().zoom() >= minZoom) {
73648 service.loadImages(projection);
73655 drawImages.enabled = function (_) {
73656 if (!arguments.length) return svgOpenstreetcamImages.enabled;
73657 svgOpenstreetcamImages.enabled = _;
73659 if (svgOpenstreetcamImages.enabled) {
73661 context.photos().on('change.openstreetcam_images', update);
73664 context.photos().on('change.openstreetcam_images', null);
73667 dispatch.call('change');
73671 drawImages.supported = function () {
73672 return !!getService();
73679 function svgOsm(projection, context, dispatch) {
73680 var enabled = true;
73682 function drawOsm(selection) {
73683 selection.selectAll('.layer-osm').data(['covered', 'areas', 'lines', 'points', 'labels']).enter().append('g').attr('class', function (d) {
73684 return 'layer-osm ' + d;
73686 selection.selectAll('.layer-osm.points').selectAll('.points-group').data(['points', 'midpoints', 'vertices', 'turns']).enter().append('g').attr('class', function (d) {
73687 return 'points-group ' + d;
73691 function showLayer() {
73692 var layer = context.surface().selectAll('.data-layer.osm');
73694 layer.classed('disabled', false).style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
73695 dispatch.call('change');
73699 function hideLayer() {
73700 var layer = context.surface().selectAll('.data-layer.osm');
73702 layer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
73703 layer.classed('disabled', true);
73704 dispatch.call('change');
73708 drawOsm.enabled = function (val) {
73709 if (!arguments.length) return enabled;
73718 dispatch.call('change');
73725 var _notesEnabled = false;
73729 function svgNotes(projection, context, dispatch$1) {
73731 dispatch$1 = dispatch('change');
73734 var throttledRedraw = throttle(function () {
73735 dispatch$1.call('change');
73739 var touchLayer = select(null);
73740 var drawLayer = select(null);
73741 var _notesVisible = false;
73743 function markerPath(selection, klass) {
73744 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');
73745 } // Loosely-coupled osm service for fetching notes.
73748 function getService() {
73749 if (services.osm && !_osmService) {
73750 _osmService = services.osm;
73752 _osmService.on('loadedNotes', throttledRedraw);
73753 } else if (!services.osm && _osmService) {
73754 _osmService = null;
73757 return _osmService;
73758 } // Show the notes
73761 function editOn() {
73762 if (!_notesVisible) {
73763 _notesVisible = true;
73764 drawLayer.style('display', 'block');
73766 } // Immediately remove the notes and their touch targets
73769 function editOff() {
73770 if (_notesVisible) {
73771 _notesVisible = false;
73772 drawLayer.style('display', 'none');
73773 drawLayer.selectAll('.note').remove();
73774 touchLayer.selectAll('.note').remove();
73776 } // Enable the layer. This shows the notes and transitions them to visible.
73779 function layerOn() {
73781 drawLayer.style('opacity', 0).transition().duration(250).style('opacity', 1).on('end interrupt', function () {
73782 dispatch$1.call('change');
73784 } // Disable the layer. This transitions the layer invisible and then hides the notes.
73787 function layerOff() {
73788 throttledRedraw.cancel();
73789 drawLayer.interrupt();
73790 touchLayer.selectAll('.note').remove();
73791 drawLayer.transition().duration(250).style('opacity', 0).on('end interrupt', function () {
73793 dispatch$1.call('change');
73795 } // Update the note markers
73798 function updateMarkers() {
73799 if (!_notesVisible || !_notesEnabled) return;
73800 var service = getService();
73801 var selectedID = context.selectedNoteID();
73802 var data = service ? service.notes(projection) : [];
73803 var getTransform = svgPointTransform(projection); // Draw markers..
73805 var notes = drawLayer.selectAll('.note').data(data, function (d) {
73806 return d.status + d.id;
73809 notes.exit().remove(); // enter
73811 var notesEnter = notes.enter().append('g').attr('class', function (d) {
73812 return 'note note-' + d.id + ' ' + d.status;
73813 }).classed('new', function (d) {
73816 notesEnter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
73817 notesEnter.append('path').call(markerPath, 'shadow');
73818 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');
73819 notesEnter.selectAll('.icon-annotation').data(function (d) {
73821 }).enter().append('use').attr('class', 'icon-annotation').attr('width', '10px').attr('height', '10px').attr('x', '-3px').attr('y', '-19px').attr('xlink:href', function (d) {
73822 return '#iD-icon-' + (d.id < 0 ? 'plus' : d.status === 'open' ? 'close' : 'apply');
73825 notes.merge(notesEnter).sort(sortY).classed('selected', function (d) {
73826 var mode = context.mode();
73827 var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging
73829 return !isMoving && d.id === selectedID;
73830 }).attr('transform', getTransform); // Draw targets..
73832 if (touchLayer.empty()) return;
73833 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
73834 var targets = touchLayer.selectAll('.note').data(data, function (d) {
73838 targets.exit().remove(); // enter/update
73840 targets.enter().append('rect').attr('width', '20px').attr('height', '20px').attr('x', '-8px').attr('y', '-22px').merge(targets).sort(sortY).attr('class', function (d) {
73841 var newClass = d.id < 0 ? 'new' : '';
73842 return 'note target note-' + d.id + ' ' + fillClass + newClass;
73843 }).attr('transform', getTransform);
73845 function sortY(a, b) {
73846 return a.id === selectedID ? 1 : b.id === selectedID ? -1 : b.loc[1] - a.loc[1];
73848 } // Draw the notes layer and schedule loading notes and updating markers.
73851 function drawNotes(selection) {
73852 var service = getService();
73853 var surface = context.surface();
73855 if (surface && !surface.empty()) {
73856 touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
73859 drawLayer = selection.selectAll('.layer-notes').data(service ? [0] : []);
73860 drawLayer.exit().remove();
73861 drawLayer = drawLayer.enter().append('g').attr('class', 'layer-notes').style('display', _notesEnabled ? 'block' : 'none').merge(drawLayer);
73863 if (_notesEnabled) {
73864 if (service && ~~context.map().zoom() >= minZoom) {
73866 service.loadNotes(projection);
73872 } // Toggles the layer on and off
73875 drawNotes.enabled = function (val) {
73876 if (!arguments.length) return _notesEnabled;
73877 _notesEnabled = val;
73879 if (_notesEnabled) {
73884 if (context.selectedNoteID()) {
73885 context.enter(modeBrowse(context));
73889 dispatch$1.call('change');
73896 function svgTouch() {
73897 function drawTouch(selection) {
73898 selection.selectAll('.layer-touch').data(['areas', 'lines', 'points', 'turns', 'markers']).enter().append('g').attr('class', function (d) {
73899 return 'layer-touch ' + d;
73906 function refresh(selection, node) {
73907 var cr = node.getBoundingClientRect();
73908 var prop = [cr.width, cr.height];
73909 selection.property('__dimensions__', prop);
73913 function utilGetDimensions(selection, force) {
73914 if (!selection || selection.empty()) {
73918 var node = selection.node(),
73919 cached = selection.property('__dimensions__');
73920 return !cached || force ? refresh(selection, node) : cached;
73922 function utilSetDimensions(selection, dimensions) {
73923 if (!selection || selection.empty()) {
73927 var node = selection.node();
73929 if (dimensions === null) {
73930 refresh(selection, node);
73934 return selection.property('__dimensions__', [dimensions[0], dimensions[1]]).attr('width', dimensions[0]).attr('height', dimensions[1]);
73937 function svgLayers(projection, context) {
73938 var dispatch$1 = dispatch('change');
73939 var svg = select(null);
73942 layer: svgOsm(projection, context, dispatch$1)
73945 layer: svgNotes(projection, context, dispatch$1)
73948 layer: svgData(projection, context, dispatch$1)
73951 layer: svgKeepRight(projection, context, dispatch$1)
73954 layer: svgImproveOSM(projection, context, dispatch$1)
73957 layer: svgOsmose(projection, context, dispatch$1)
73960 layer: svgStreetside(projection, context, dispatch$1)
73963 layer: svgMapillaryImages(projection, context, dispatch$1)
73965 id: 'mapillary-position',
73966 layer: svgMapillaryPosition(projection, context)
73968 id: 'mapillary-map-features',
73969 layer: svgMapillaryMapFeatures(projection, context, dispatch$1)
73971 id: 'mapillary-signs',
73972 layer: svgMapillarySigns(projection, context, dispatch$1)
73974 id: 'openstreetcam',
73975 layer: svgOpenstreetcamImages(projection, context, dispatch$1)
73978 layer: svgDebug(projection, context)
73981 layer: svgGeolocate(projection)
73987 function drawLayers(selection) {
73988 svg = selection.selectAll('.surface').data([0]);
73989 svg = svg.enter().append('svg').attr('class', 'surface').merge(svg);
73990 var defs = svg.selectAll('.surface-defs').data([0]);
73991 defs.enter().append('defs').attr('class', 'surface-defs');
73992 var groups = svg.selectAll('.data-layer').data(_layers);
73993 groups.exit().remove();
73994 groups.enter().append('g').attr('class', function (d) {
73995 return 'data-layer ' + d.id;
73996 }).merge(groups).each(function (d) {
73997 select(this).call(d.layer);
74001 drawLayers.all = function () {
74005 drawLayers.layer = function (id) {
74006 var obj = _layers.find(function (o) {
74007 return o.id === id;
74010 return obj && obj.layer;
74013 drawLayers.only = function (what) {
74014 var arr = [].concat(what);
74016 var all = _layers.map(function (layer) {
74020 return drawLayers.remove(utilArrayDifference(all, arr));
74023 drawLayers.remove = function (what) {
74024 var arr = [].concat(what);
74025 arr.forEach(function (id) {
74026 _layers = _layers.filter(function (o) {
74027 return o.id !== id;
74030 dispatch$1.call('change');
74034 drawLayers.add = function (what) {
74035 var arr = [].concat(what);
74036 arr.forEach(function (obj) {
74037 if ('id' in obj && 'layer' in obj) {
74041 dispatch$1.call('change');
74045 drawLayers.dimensions = function (val) {
74046 if (!arguments.length) return utilGetDimensions(svg);
74047 utilSetDimensions(svg, val);
74051 return utilRebind(drawLayers, dispatch$1, 'on');
74054 function svgLines(projection, context) {
74055 var detected = utilDetect();
74056 var highway_stack = {
74071 function drawTargets(selection, graph, entities, filter) {
74072 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74073 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
74074 var getPath = svgPath(projection).geojson;
74075 var activeID = context.activeID();
74076 var base = context.history().base(); // The targets and nopes will be MultiLineString sub-segments of the ways
74082 entities.forEach(function (way) {
74083 var features = svgSegmentWay(way, graph, activeID);
74084 data.targets.push.apply(data.targets, features.passive);
74085 data.nopes.push.apply(data.nopes, features.active);
74086 }); // Targets allow hover and vertex snapping
74088 var targetData = data.targets.filter(getPath);
74089 var targets = selection.selectAll('.line.target-allowed').filter(function (d) {
74090 return filter(d.properties.entity);
74091 }).data(targetData, function key(d) {
74095 targets.exit().remove();
74097 var segmentWasEdited = function segmentWasEdited(d) {
74098 var wayID = d.properties.entity.id; // if the whole line was edited, don't draw segment changes
74100 if (!base.entities[wayID] || !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
74104 return d.properties.nodes.some(function (n) {
74105 return !base.entities[n.id] || !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
74110 targets.enter().append('path').merge(targets).attr('d', getPath).attr('class', function (d) {
74111 return 'way line target target-allowed ' + targetClass + d.id;
74112 }).classed('segment-edited', segmentWasEdited); // NOPE
74114 var nopeData = data.nopes.filter(getPath);
74115 var nopes = selection.selectAll('.line.target-nope').filter(function (d) {
74116 return filter(d.properties.entity);
74117 }).data(nopeData, function key(d) {
74121 nopes.exit().remove(); // enter/update
74123 nopes.enter().append('path').merge(nopes).attr('d', getPath).attr('class', function (d) {
74124 return 'way line target target-nope ' + nopeClass + d.id;
74125 }).classed('segment-edited', segmentWasEdited);
74128 function drawLines(selection, graph, entities, filter) {
74129 var base = context.history().base();
74131 function waystack(a, b) {
74132 var selected = context.selectedIDs();
74133 var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
74134 var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
74136 if (a.tags.highway) {
74137 scoreA -= highway_stack[a.tags.highway];
74140 if (b.tags.highway) {
74141 scoreB -= highway_stack[b.tags.highway];
74144 return scoreA - scoreB;
74147 function drawLineGroup(selection, klass, isSelected) {
74148 // Note: Don't add `.selected` class in draw modes
74149 var mode = context.mode();
74150 var isDrawing = mode && /^draw/.test(mode.id);
74151 var selectedClass = !isDrawing && isSelected ? 'selected ' : '';
74152 var lines = selection.selectAll('path').filter(filter).data(getPathData(isSelected), osmEntity.key);
74153 lines.exit().remove(); // Optimization: Call expensive TagClasses only on enter selection. This
74154 // works because osmEntity.key is defined to include the entity v attribute.
74156 lines.enter().append('path').attr('class', function (d) {
74157 var prefix = 'way line'; // if this line isn't styled by its own tags
74159 if (!d.hasInterestingTags()) {
74160 var parentRelations = graph.parentRelations(d);
74161 var parentMultipolygons = parentRelations.filter(function (relation) {
74162 return relation.isMultipolygon();
74163 }); // and if it's a member of at least one multipolygon relation
74165 if (parentMultipolygons.length > 0 && // and only multipolygon relations
74166 parentRelations.length === parentMultipolygons.length) {
74167 // then fudge the classes to style this as an area edge
74168 prefix = 'relation area';
74172 var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
74173 return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
74174 }).classed('added', function (d) {
74175 return !base.entities[d.id];
74176 }).classed('geometry-edited', function (d) {
74177 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
74178 }).classed('retagged', function (d) {
74179 return graph.entities[d.id] && base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74180 }).call(svgTagClasses()).merge(lines).sort(waystack).attr('d', getPath).call(svgTagClasses().tags(svgRelationMemberTags(graph)));
74184 function getPathData(isSelected) {
74185 return function () {
74186 var layer = this.parentNode.__data__;
74187 var data = pathdata[layer] || [];
74188 return data.filter(function (d) {
74189 if (isSelected) return context.selectedIDs().indexOf(d.id) !== -1;else return context.selectedIDs().indexOf(d.id) === -1;
74194 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
74195 var markergroup = layergroup.selectAll('g.' + groupclass).data([pathclass]);
74196 markergroup = markergroup.enter().append('g').attr('class', groupclass).merge(markergroup);
74197 var markers = markergroup.selectAll('path').filter(filter).data(function data() {
74198 return groupdata[this.parentNode.__data__] || [];
74199 }, function key(d) {
74200 return [d.id, d.index];
74202 markers.exit().remove();
74203 markers = markers.enter().append('path').attr('class', pathclass).merge(markers).attr('marker-mid', marker).attr('d', function (d) {
74208 markers.each(function () {
74209 this.parentNode.insertBefore(this, this);
74214 var getPath = svgPath(projection, graph);
74216 var onewaydata = {};
74217 var sideddata = {};
74218 var oldMultiPolygonOuters = {};
74220 for (var i = 0; i < entities.length; i++) {
74221 var entity = entities[i];
74222 var outer = osmOldMultipolygonOuterMember(entity, graph);
74225 ways.push(entity.mergeTags(outer.tags));
74226 oldMultiPolygonOuters[outer.id] = true;
74227 } else if (entity.geometry(graph) === 'line') {
74232 ways = ways.filter(getPath);
74233 var pathdata = utilArrayGroupBy(ways, function (way) {
74234 return way.layer();
74236 Object.keys(pathdata).forEach(function (k) {
74237 var v = pathdata[k];
74238 var onewayArr = v.filter(function (d) {
74239 return d.isOneWay();
74241 var onewaySegments = svgMarkerSegments(projection, graph, 35, function shouldReverse(entity) {
74242 return entity.tags.oneway === '-1';
74243 }, function bothDirections(entity) {
74244 return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
74246 onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
74247 var sidedArr = v.filter(function (d) {
74248 return d.isSided();
74250 var sidedSegments = svgMarkerSegments(projection, graph, 30, function shouldReverse() {
74252 }, function bothDirections() {
74255 sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
74257 var covered = selection.selectAll('.layer-osm.covered'); // under areas
74259 var uncovered = selection.selectAll('.layer-osm.lines'); // over areas
74261 var touchLayer = selection.selectAll('.layer-touch.lines'); // Draw lines..
74263 [covered, uncovered].forEach(function (selection) {
74264 var range$1 = selection === covered ? range(-10, 0) : range(0, 11);
74265 var layergroup = selection.selectAll('g.layergroup').data(range$1);
74266 layergroup = layergroup.enter().append('g').attr('class', function (d) {
74267 return 'layergroup layer' + String(d);
74268 }).merge(layergroup);
74269 layergroup.selectAll('g.linegroup').data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted']).enter().append('g').attr('class', function (d) {
74270 return 'linegroup line-' + d;
74272 layergroup.selectAll('g.line-shadow').call(drawLineGroup, 'shadow', false);
74273 layergroup.selectAll('g.line-casing').call(drawLineGroup, 'casing', false);
74274 layergroup.selectAll('g.line-stroke').call(drawLineGroup, 'stroke', false);
74275 layergroup.selectAll('g.line-shadow-highlighted').call(drawLineGroup, 'shadow', true);
74276 layergroup.selectAll('g.line-casing-highlighted').call(drawLineGroup, 'casing', true);
74277 layergroup.selectAll('g.line-stroke-highlighted').call(drawLineGroup, 'stroke', true);
74278 addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
74279 addMarkers(layergroup, 'sided', 'sidedgroup', sideddata, function marker(d) {
74280 var category = graph.entity(d.id).sidednessIdentifier();
74281 return 'url(#ideditor-sided-marker-' + category + ')';
74283 }); // Draw touch targets..
74285 touchLayer.call(drawTargets, graph, ways, filter);
74291 function svgMidpoints(projection, context) {
74292 var targetRadius = 8;
74294 function drawTargets(selection, graph, entities, filter) {
74295 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74296 var getTransform = svgPointTransform(projection).geojson;
74297 var data = entities.map(function (midpoint) {
74307 coordinates: midpoint.loc
74311 var targets = selection.selectAll('.midpoint.target').filter(function (d) {
74312 return filter(d.properties.entity);
74313 }).data(data, function key(d) {
74317 targets.exit().remove(); // enter/update
74319 targets.enter().append('circle').attr('r', targetRadius).merge(targets).attr('class', function (d) {
74320 return 'node midpoint target ' + fillClass + d.id;
74321 }).attr('transform', getTransform);
74324 function drawMidpoints(selection, graph, entities, filter, extent) {
74325 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
74326 var touchLayer = selection.selectAll('.layer-touch.points');
74327 var mode = context.mode();
74329 if (mode && mode.id !== 'select' || !context.map().withinEditableZoom()) {
74330 drawLayer.selectAll('.midpoint').remove();
74331 touchLayer.selectAll('.midpoint.target').remove();
74335 var poly = extent.polygon();
74336 var midpoints = {};
74338 for (var i = 0; i < entities.length; i++) {
74339 var entity = entities[i];
74340 if (entity.type !== 'way') continue;
74341 if (!filter(entity)) continue;
74342 if (context.selectedIDs().indexOf(entity.id) < 0) continue;
74343 var nodes = graph.childNodes(entity);
74345 for (var j = 0; j < nodes.length - 1; j++) {
74347 var b = nodes[j + 1];
74348 var id = [a.id, b.id].sort().join('-');
74350 if (midpoints[id]) {
74351 midpoints[id].parents.push(entity);
74352 } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
74353 var point = geoVecInterp(a.loc, b.loc, 0.5);
74356 if (extent.intersects(point)) {
74359 for (var k = 0; k < 4; k++) {
74360 point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
74362 if (point && geoVecLength(projection(a.loc), projection(point)) > 20 && geoVecLength(projection(b.loc), projection(point)) > 20) {
74374 edge: [a.id, b.id],
74382 function midpointFilter(d) {
74383 if (midpoints[d.id]) return true;
74385 for (var i = 0; i < d.parents.length; i++) {
74386 if (filter(d.parents[i])) {
74394 var groups = drawLayer.selectAll('.midpoint').filter(midpointFilter).data(Object.values(midpoints), function (d) {
74397 groups.exit().remove();
74398 var enter = groups.enter().insert('g', ':first-child').attr('class', 'midpoint');
74399 enter.append('polygon').attr('points', '-6,8 10,0 -6,-8').attr('class', 'shadow');
74400 enter.append('polygon').attr('points', '-3,4 5,0 -3,-4').attr('class', 'fill');
74401 groups = groups.merge(enter).attr('transform', function (d) {
74402 var translate = svgPointTransform(projection);
74403 var a = graph.entity(d.edge[0]);
74404 var b = graph.entity(d.edge[1]);
74405 var angle = geoAngle(a, b, projection) * (180 / Math.PI);
74406 return translate(d) + ' rotate(' + angle + ')';
74407 }).call(svgTagClasses().tags(function (d) {
74408 return d.parents[0].tags;
74409 })); // Propagate data bindings.
74411 groups.select('polygon.shadow');
74412 groups.select('polygon.fill'); // Draw touch targets..
74414 touchLayer.call(drawTargets, graph, Object.values(midpoints), midpointFilter);
74417 return drawMidpoints;
74420 function svgPoints(projection, context) {
74421 function markerPath(selection, klass) {
74422 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');
74425 function sortY(a, b) {
74426 return b.loc[1] - a.loc[1];
74427 } // Avoid exit/enter if we're just moving stuff around.
74428 // The node will get a new version but we only need to run the update selection.
74431 function fastEntityKey(d) {
74432 var mode = context.mode();
74433 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74434 return isMoving ? d.id : osmEntity.key(d);
74437 function drawTargets(selection, graph, entities, filter) {
74438 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74439 var getTransform = svgPointTransform(projection).geojson;
74440 var activeID = context.activeID();
74442 entities.forEach(function (node) {
74443 if (activeID === node.id) return; // draw no target on the activeID
74452 geometry: node.asGeoJSON()
74455 var targets = selection.selectAll('.point.target').filter(function (d) {
74456 return filter(d.properties.entity);
74457 }).data(data, function key(d) {
74461 targets.exit().remove(); // enter/update
74463 targets.enter().append('rect').attr('x', -10).attr('y', -26).attr('width', 20).attr('height', 30).merge(targets).attr('class', function (d) {
74464 return 'node point target ' + fillClass + d.id;
74465 }).attr('transform', getTransform);
74468 function drawPoints(selection, graph, entities, filter) {
74469 var wireframe = context.surface().classed('fill-wireframe');
74470 var zoom = geoScaleToZoom(projection.scale());
74471 var base = context.history().base(); // Points with a direction will render as vertices at higher zooms..
74473 function renderAsPoint(entity) {
74474 return entity.geometry(graph) === 'point' && !(zoom >= 18 && entity.directions(graph, projection).length);
74475 } // All points will render as vertices in wireframe mode too..
74478 var points = wireframe ? [] : entities.filter(renderAsPoint);
74479 points.sort(sortY);
74480 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
74481 var touchLayer = selection.selectAll('.layer-touch.points'); // Draw points..
74483 var groups = drawLayer.selectAll('g.point').filter(filter).data(points, fastEntityKey);
74484 groups.exit().remove();
74485 var enter = groups.enter().append('g').attr('class', function (d) {
74486 return 'node point ' + d.id;
74488 enter.append('path').call(markerPath, 'shadow');
74489 enter.append('ellipse').attr('cx', 0.5).attr('cy', 1).attr('rx', 6.5).attr('ry', 3).attr('class', 'stroke');
74490 enter.append('path').call(markerPath, 'stroke');
74491 enter.append('use').attr('transform', 'translate(-5, -19)').attr('class', 'icon').attr('width', '11px').attr('height', '11px');
74492 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('added', function (d) {
74493 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
74494 }).classed('moved', function (d) {
74495 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
74496 }).classed('retagged', function (d) {
74497 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74498 }).call(svgTagClasses());
74499 groups.select('.shadow'); // propagate bound data
74501 groups.select('.stroke'); // propagate bound data
74503 groups.select('.icon') // propagate bound data
74504 .attr('xlink:href', function (entity) {
74505 var preset = _mainPresetIndex.match(entity, graph);
74506 var picon = preset && preset.icon;
74511 var isMaki = /^maki-/.test(picon);
74512 return '#' + picon + (isMaki ? '-11' : '');
74514 }); // Draw touch targets..
74516 touchLayer.call(drawTargets, graph, points, filter);
74522 function svgTurns(projection, context) {
74523 function icon(turn) {
74524 var u = turn.u ? '-u' : '';
74525 if (turn.no) return '#iD-turn-no' + u;
74526 if (turn.only) return '#iD-turn-only' + u;
74527 return '#iD-turn-yes' + u;
74530 function drawTurns(selection, graph, turns) {
74531 function turnTransform(d) {
74533 var toWay = graph.entity(d.to.way);
74534 var toPoints = graph.childNodes(toWay).map(function (n) {
74536 }).map(projection);
74537 var toLength = geoPathLength(toPoints);
74538 var mid = toLength / 2; // midpoint of destination way
74540 var toNode = graph.entity(d.to.node);
74541 var toVertex = graph.entity(d.to.vertex);
74542 var a = geoAngle(toVertex, toNode, projection);
74543 var o = projection(toVertex.loc);
74544 var r = d.u ? 0 // u-turn: no radius
74545 : !toWay.__via ? pxRadius // leaf way: put marker at pxRadius
74546 : Math.min(mid, pxRadius); // via way: prefer pxRadius, fallback to mid for very short ways
74548 return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' + 'rotate(' + a * 180 / Math.PI + ')';
74551 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
74552 var touchLayer = selection.selectAll('.layer-touch.turns'); // Draw turns..
74554 var groups = drawLayer.selectAll('g.turn').data(turns, function (d) {
74558 groups.exit().remove(); // enter
74560 var groupsEnter = groups.enter().append('g').attr('class', function (d) {
74561 return 'turn ' + d.key;
74563 var turnsEnter = groupsEnter.filter(function (d) {
74566 turnsEnter.append('rect').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74567 turnsEnter.append('use').attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74568 var uEnter = groupsEnter.filter(function (d) {
74571 uEnter.append('circle').attr('r', '16');
74572 uEnter.append('use').attr('transform', 'translate(-16, -16)').attr('width', '32').attr('height', '32'); // update
74574 groups = groups.merge(groupsEnter).attr('opacity', function (d) {
74575 return d.direct === false ? '0.7' : null;
74576 }).attr('transform', turnTransform);
74577 groups.select('use').attr('xlink:href', icon);
74578 groups.select('rect'); // propagate bound data
74580 groups.select('circle'); // propagate bound data
74581 // Draw touch targets..
74583 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74584 groups = touchLayer.selectAll('g.turn').data(turns, function (d) {
74588 groups.exit().remove(); // enter
74590 groupsEnter = groups.enter().append('g').attr('class', function (d) {
74591 return 'turn ' + d.key;
74593 turnsEnter = groupsEnter.filter(function (d) {
74596 turnsEnter.append('rect').attr('class', 'target ' + fillClass).attr('transform', 'translate(-22, -12)').attr('width', '44').attr('height', '24');
74597 uEnter = groupsEnter.filter(function (d) {
74600 uEnter.append('circle').attr('class', 'target ' + fillClass).attr('r', '16'); // update
74602 groups = groups.merge(groupsEnter).attr('transform', turnTransform);
74603 groups.select('rect'); // propagate bound data
74605 groups.select('circle'); // propagate bound data
74613 function svgVertices(projection, context) {
74615 // z16-, z17, z18+, w/icon
74616 shadow: [6, 7.5, 7.5, 12],
74617 stroke: [2.5, 3.5, 3.5, 8],
74618 fill: [1, 1.5, 1.5, 1.5]
74621 var _currHoverTarget;
74623 var _currPersistent = {};
74624 var _currHover = {};
74625 var _prevHover = {};
74626 var _currSelected = {};
74627 var _prevSelected = {};
74630 function sortY(a, b) {
74631 return b.loc[1] - a.loc[1];
74632 } // Avoid exit/enter if we're just moving stuff around.
74633 // The node will get a new version but we only need to run the update selection.
74636 function fastEntityKey(d) {
74637 var mode = context.mode();
74638 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74639 return isMoving ? d.id : osmEntity.key(d);
74642 function draw(selection, graph, vertices, sets, filter) {
74649 var directions = {};
74650 var wireframe = context.surface().classed('fill-wireframe');
74651 var zoom = geoScaleToZoom(projection.scale());
74652 var z = zoom < 17 ? 0 : zoom < 18 ? 1 : 2;
74653 var activeID = context.activeID();
74654 var base = context.history().base();
74656 function getIcon(d) {
74657 // always check latest entity, as fastEntityKey avoids enter/exit now
74658 var entity = graph.entity(d.id);
74659 if (entity.id in icons) return icons[entity.id];
74660 icons[entity.id] = entity.hasInterestingTags() && _mainPresetIndex.match(entity, graph).icon;
74661 return icons[entity.id];
74662 } // memoize directions results, return false for empty arrays (for use in filter)
74665 function getDirections(entity) {
74666 if (entity.id in directions) return directions[entity.id];
74667 var angles = entity.directions(graph, projection);
74668 directions[entity.id] = angles.length ? angles : false;
74672 function updateAttributes(selection) {
74673 ['shadow', 'stroke', 'fill'].forEach(function (klass) {
74674 var rads = radiuses[klass];
74675 selection.selectAll('.' + klass).each(function (entity) {
74676 var i = z && getIcon(entity);
74677 var r = rads[i ? 3 : z]; // slightly increase the size of unconnected endpoints #3775
74679 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
74683 if (klass === 'shadow') {
74684 // remember this value, so we don't need to
74685 _radii[entity.id] = r; // recompute it when we draw the touch targets
74688 select(this).attr('r', r).attr('visibility', i && klass === 'fill' ? 'hidden' : null);
74693 vertices.sort(sortY);
74694 var groups = selection.selectAll('g.vertex').filter(filter).data(vertices, fastEntityKey); // exit
74696 groups.exit().remove(); // enter
74698 var enter = groups.enter().append('g').attr('class', function (d) {
74699 return 'node vertex ' + d.id;
74701 enter.append('circle').attr('class', 'shadow');
74702 enter.append('circle').attr('class', 'stroke'); // Vertices with tags get a fill.
74704 enter.filter(function (d) {
74705 return d.hasInterestingTags();
74706 }).append('circle').attr('class', 'fill'); // update
74708 groups = groups.merge(enter).attr('transform', svgPointTransform(projection)).classed('sibling', function (d) {
74709 return d.id in sets.selected;
74710 }).classed('shared', function (d) {
74711 return graph.isShared(d);
74712 }).classed('endpoint', function (d) {
74713 return d.isEndpoint(graph);
74714 }).classed('added', function (d) {
74715 return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
74716 }).classed('moved', function (d) {
74717 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
74718 }).classed('retagged', function (d) {
74719 return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
74720 }).call(updateAttributes); // Vertices with icons get a `use`.
74722 var iconUse = groups.selectAll('.icon').data(function data(d) {
74723 return zoom >= 17 && getIcon(d) ? [d] : [];
74724 }, fastEntityKey); // exit
74726 iconUse.exit().remove(); // enter
74728 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) {
74729 var picon = getIcon(d);
74730 var isMaki = /^maki-/.test(picon);
74731 return '#' + picon + (isMaki ? '-11' : '');
74732 }); // Vertices with directions get viewfields
74734 var dgroups = groups.selectAll('.viewfieldgroup').data(function data(d) {
74735 return zoom >= 18 && getDirections(d) ? [d] : [];
74736 }, fastEntityKey); // exit
74738 dgroups.exit().remove(); // enter/update
74740 dgroups = dgroups.enter().insert('g', '.shadow').attr('class', 'viewfieldgroup').merge(dgroups);
74741 var viewfields = dgroups.selectAll('.viewfield').data(getDirections, function key(d) {
74742 return osmEntity.key(d);
74745 viewfields.exit().remove(); // enter/update
74747 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) {
74748 return 'rotate(' + d + ')';
74752 function drawTargets(selection, graph, entities, filter) {
74753 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
74754 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
74755 var getTransform = svgPointTransform(projection).geojson;
74756 var activeID = context.activeID();
74761 entities.forEach(function (node) {
74762 if (activeID === node.id) return; // draw no target on the activeID
74764 var vertexType = svgPassiveVertex(node, graph, activeID);
74766 if (vertexType !== 0) {
74767 // passive or adjacent - allow to connect
74768 data.targets.push({
74775 geometry: node.asGeoJSON()
74780 id: node.id + '-nope',
74786 geometry: node.asGeoJSON()
74789 }); // Targets allow hover and vertex snapping
74791 var targets = selection.selectAll('.vertex.target-allowed').filter(function (d) {
74792 return filter(d.properties.entity);
74793 }).data(data.targets, function key(d) {
74797 targets.exit().remove(); // enter/update
74799 targets.enter().append('circle').attr('r', function (d) {
74800 return _radii[d.id] || radiuses.shadow[3];
74801 }).merge(targets).attr('class', function (d) {
74802 return 'node vertex target target-allowed ' + targetClass + d.id;
74803 }).attr('transform', getTransform); // NOPE
74805 var nopes = selection.selectAll('.vertex.target-nope').filter(function (d) {
74806 return filter(d.properties.entity);
74807 }).data(data.nopes, function key(d) {
74811 nopes.exit().remove(); // enter/update
74813 nopes.enter().append('circle').attr('r', function (d) {
74814 return _radii[d.properties.entity.id] || radiuses.shadow[3];
74815 }).merge(nopes).attr('class', function (d) {
74816 return 'node vertex target target-nope ' + nopeClass + d.id;
74817 }).attr('transform', getTransform);
74818 } // Points can also render as vertices:
74819 // 1. in wireframe mode or
74820 // 2. at higher zooms if they have a direction
74823 function renderAsVertex(entity, graph, wireframe, zoom) {
74824 var geometry = entity.geometry(graph);
74825 return geometry === 'vertex' || geometry === 'point' && (wireframe || zoom >= 18 && entity.directions(graph, projection).length);
74828 function isEditedNode(node, base, head) {
74829 var baseNode = base.entities[node.id];
74830 var headNode = head.entities[node.id];
74831 return !headNode || !baseNode || !fastDeepEqual(headNode.tags, baseNode.tags) || !fastDeepEqual(headNode.loc, baseNode.loc);
74834 function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
74838 function addChildVertices(entity) {
74839 // avoid redundant work and infinite recursion of circular relations
74840 if (seenIds[entity.id]) return;
74841 seenIds[entity.id] = true;
74842 var geometry = entity.geometry(graph);
74844 if (!context.features().isHiddenFeature(entity, graph, geometry)) {
74847 if (entity.type === 'way') {
74848 for (i = 0; i < entity.nodes.length; i++) {
74849 var child = graph.hasEntity(entity.nodes[i]);
74852 addChildVertices(child);
74855 } else if (entity.type === 'relation') {
74856 for (i = 0; i < entity.members.length; i++) {
74857 var member = graph.hasEntity(entity.members[i].id);
74860 addChildVertices(member);
74863 } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
74864 results[entity.id] = entity;
74869 ids.forEach(function (id) {
74870 var entity = graph.hasEntity(id);
74871 if (!entity) return;
74873 if (entity.type === 'node') {
74874 if (renderAsVertex(entity, graph, wireframe, zoom)) {
74875 results[entity.id] = entity;
74876 graph.parentWays(entity).forEach(function (entity) {
74877 addChildVertices(entity);
74882 addChildVertices(entity);
74888 function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
74889 var wireframe = context.surface().classed('fill-wireframe');
74890 var visualDiff = context.surface().classed('highlight-edited');
74891 var zoom = geoScaleToZoom(projection.scale());
74892 var mode = context.mode();
74893 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
74894 var base = context.history().base();
74895 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
74896 var touchLayer = selection.selectAll('.layer-touch.points');
74899 _currPersistent = {};
74901 } // Collect important vertices from the `entities` list..
74902 // (during a partial redraw, it will not contain everything)
74905 for (var i = 0; i < entities.length; i++) {
74906 var entity = entities[i];
74907 var geometry = entity.geometry(graph);
74908 var keep = false; // a point that looks like a vertex..
74910 if (geometry === 'point' && renderAsVertex(entity, graph, wireframe, zoom)) {
74911 _currPersistent[entity.id] = entity;
74912 keep = true; // a vertex of some importance..
74913 } else if (geometry === 'vertex' && (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph) || visualDiff && isEditedNode(entity, base, graph))) {
74914 _currPersistent[entity.id] = entity;
74916 } // whatever this is, it's not a persistent vertex..
74919 if (!keep && !fullRedraw) {
74920 delete _currPersistent[entity.id];
74922 } // 3 sets of vertices to consider:
74926 persistent: _currPersistent,
74927 // persistent = important vertices (render always)
74928 selected: _currSelected,
74929 // selected + siblings of selected (render always)
74930 hovered: _currHover // hovered + siblings of hovered (render only in draw modes)
74933 var all = Object.assign({}, isMoving ? _currHover : {}, _currSelected, _currPersistent); // Draw the vertices..
74934 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
74935 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
74937 var filterRendered = function filterRendered(d) {
74938 return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
74941 drawLayer.call(draw, graph, currentVisible(all), sets, filterRendered); // Draw touch targets..
74942 // When drawing, render all targets (not just those affected by a partial redraw)
74944 var filterTouch = function filterTouch(d) {
74945 return isMoving ? true : filterRendered(d);
74948 touchLayer.call(drawTargets, graph, currentVisible(all), filterTouch);
74950 function currentVisible(which) {
74951 return Object.keys(which).map(graph.hasEntity, graph) // the current version of this entity
74952 .filter(function (entity) {
74953 return entity && entity.intersects(extent, graph);
74956 } // partial redraw - only update the selected items..
74959 drawVertices.drawSelected = function (selection, graph, extent) {
74960 var wireframe = context.surface().classed('fill-wireframe');
74961 var zoom = geoScaleToZoom(projection.scale());
74962 _prevSelected = _currSelected || {};
74964 if (context.map().isInWideSelection()) {
74965 _currSelected = {};
74966 context.selectedIDs().forEach(function (id) {
74967 var entity = graph.hasEntity(id);
74968 if (!entity) return;
74970 if (entity.type === 'node') {
74971 if (renderAsVertex(entity, graph, wireframe, zoom)) {
74972 _currSelected[entity.id] = entity;
74977 _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
74978 } // note that drawVertices will add `_currSelected` automatically if needed..
74981 var filter = function filter(d) {
74982 return d.id in _prevSelected;
74985 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
74986 }; // partial redraw - only update the hovered items..
74989 drawVertices.drawHover = function (selection, graph, target, extent) {
74990 if (target === _currHoverTarget) return; // continue only if something changed
74992 var wireframe = context.surface().classed('fill-wireframe');
74993 var zoom = geoScaleToZoom(projection.scale());
74994 _prevHover = _currHover || {};
74995 _currHoverTarget = target;
74996 var entity = target && target.properties && target.properties.entity;
74999 _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
75002 } // note that drawVertices will add `_currHover` automatically if needed..
75005 var filter = function filter(d) {
75006 return d.id in _prevHover;
75009 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
75012 return drawVertices;
75015 function utilBindOnce(target, type, listener, capture) {
75016 var typeOnce = type + '.once';
75019 target.on(typeOnce, null);
75020 listener.apply(this, arguments);
75023 target.on(typeOnce, one, capture);
75027 function defaultFilter$2(d3_event) {
75028 return !d3_event.ctrlKey && !d3_event.button;
75031 function defaultExtent$1() {
75034 if (e instanceof SVGElement) {
75035 e = e.ownerSVGElement || e;
75037 if (e.hasAttribute('viewBox')) {
75038 e = e.viewBox.baseVal;
75039 return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
75042 return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
75045 return [[0, 0], [e.clientWidth, e.clientHeight]];
75048 function defaultWheelDelta$1(d3_event) {
75049 return -d3_event.deltaY * (d3_event.deltaMode === 1 ? 0.05 : d3_event.deltaMode ? 1 : 0.002);
75052 function defaultConstrain$1(transform, extent, translateExtent) {
75053 var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
75054 dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
75055 dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
75056 dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
75057 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));
75060 function utilZoomPan() {
75061 var filter = defaultFilter$2,
75062 extent = defaultExtent$1,
75063 constrain = defaultConstrain$1,
75064 wheelDelta = defaultWheelDelta$1,
75065 scaleExtent = [0, Infinity],
75066 translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
75067 interpolate = interpolateZoom,
75068 dispatch$1 = dispatch('start', 'zoom', 'end'),
75070 _transform = identity$2,
75073 function zoom(selection) {
75074 selection.on('pointerdown.zoom', pointerdown).on('wheel.zoom', wheeled).style('touch-action', 'none').style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
75075 select(window).on('pointermove.zoompan', pointermove).on('pointerup.zoompan pointercancel.zoompan', pointerup);
75078 zoom.transform = function (collection, transform, point) {
75079 var selection = collection.selection ? collection.selection() : collection;
75081 if (collection !== selection) {
75082 schedule(collection, transform, point);
75084 selection.interrupt().each(function () {
75085 gesture(this, arguments).start(null).zoom(null, null, typeof transform === 'function' ? transform.apply(this, arguments) : transform).end(null);
75090 zoom.scaleBy = function (selection, k, p) {
75091 zoom.scaleTo(selection, function () {
75092 var k0 = _transform.k,
75093 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
75098 zoom.scaleTo = function (selection, k, p) {
75099 zoom.transform(selection, function () {
75100 var e = extent.apply(this, arguments),
75102 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
75103 p1 = t0.invert(p0),
75104 k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
75105 return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
75109 zoom.translateBy = function (selection, x, y) {
75110 zoom.transform(selection, function () {
75111 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);
75115 zoom.translateTo = function (selection, x, y, p) {
75116 zoom.transform(selection, function () {
75117 var e = extent.apply(this, arguments),
75119 p0 = !p ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
75120 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);
75124 function scale(transform, k) {
75125 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
75126 return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
75129 function translate(transform, p0, p1) {
75130 var x = p0[0] - p1[0] * transform.k,
75131 y = p0[1] - p1[1] * transform.k;
75132 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
75135 function centroid(extent) {
75136 return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
75139 function schedule(transition, transform, point) {
75140 transition.on('start.zoom', function () {
75141 gesture(this, arguments).start(null);
75142 }).on('interrupt.zoom end.zoom', function () {
75143 gesture(this, arguments).end(null);
75144 }).tween('zoom', function () {
75147 g = gesture(that, args),
75148 e = extent.apply(that, args),
75149 p = !point ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
75150 w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
75152 b = typeof transform === 'function' ? transform.apply(that, args) : transform,
75153 i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
75154 return function (t) {
75155 if (t === 1) t = b; // Avoid rounding error on end.
75159 t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
75161 g.zoom(null, null, t);
75166 function gesture(that, args, clean) {
75167 return !clean && _activeGesture || new Gesture(that, args);
75170 function Gesture(that, args) {
75174 this.extent = extent.apply(that, args);
75177 Gesture.prototype = {
75178 start: function start(d3_event) {
75179 if (++this.active === 1) {
75180 _activeGesture = this;
75181 dispatch$1.call('start', this, d3_event);
75186 zoom: function zoom(d3_event, key, transform) {
75187 if (this.mouse && key !== 'mouse') this.mouse[1] = transform.invert(this.mouse[0]);
75188 if (this.pointer0 && key !== 'touch') this.pointer0[1] = transform.invert(this.pointer0[0]);
75189 if (this.pointer1 && key !== 'touch') this.pointer1[1] = transform.invert(this.pointer1[0]);
75190 _transform = transform;
75191 dispatch$1.call('zoom', this, d3_event, key, transform);
75194 end: function end(d3_event) {
75195 if (--this.active === 0) {
75196 _activeGesture = null;
75197 dispatch$1.call('end', this, d3_event);
75204 function wheeled(d3_event) {
75205 if (!filter.apply(this, arguments)) return;
75206 var g = gesture(this, arguments),
75208 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
75209 p = utilFastMouse(this)(d3_event); // If the mouse is in the same location as before, reuse it.
75210 // If there were recent wheel events, reset the wheel idle timeout.
75213 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
75214 g.mouse[1] = t.invert(g.mouse[0] = p);
75217 clearTimeout(g.wheel); // Otherwise, capture the mouse point and location at the start.
75219 g.mouse = [p, t.invert(p)];
75224 d3_event.preventDefault();
75225 d3_event.stopImmediatePropagation();
75226 g.wheel = setTimeout(wheelidled, _wheelDelay);
75227 g.zoom(d3_event, 'mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
75229 function wheelidled() {
75235 var _downPointerIDs = new Set();
75237 var _pointerLocGetter;
75239 function pointerdown(d3_event) {
75240 _downPointerIDs.add(d3_event.pointerId);
75242 if (!filter.apply(this, arguments)) return;
75243 var g = gesture(this, arguments, _downPointerIDs.size === 1);
75245 d3_event.stopImmediatePropagation();
75246 _pointerLocGetter = utilFastMouse(this);
75248 var loc = _pointerLocGetter(d3_event);
75250 var p = [loc, _transform.invert(loc), d3_event.pointerId];
75255 } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
75265 function pointermove(d3_event) {
75266 if (!_downPointerIDs.has(d3_event.pointerId)) return;
75267 if (!_activeGesture || !_pointerLocGetter) return;
75268 var g = gesture(this, arguments);
75269 var isPointer0 = g.pointer0 && g.pointer0[2] === d3_event.pointerId;
75270 var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === d3_event.pointerId;
75272 if ((isPointer0 || isPointer1) && 'buttons' in d3_event && !d3_event.buttons) {
75273 // The pointer went up without ending the gesture somehow, e.g.
75274 // a down mouse was moved off the map and released. End it here.
75275 if (g.pointer0) _downPointerIDs["delete"](g.pointer0[2]);
75276 if (g.pointer1) _downPointerIDs["delete"](g.pointer1[2]);
75281 d3_event.preventDefault();
75282 d3_event.stopImmediatePropagation();
75284 var loc = _pointerLocGetter(d3_event);
75287 if (isPointer0) g.pointer0[0] = loc;else if (isPointer1) g.pointer1[0] = loc;
75291 var p0 = g.pointer0[0],
75292 l0 = g.pointer0[1],
75293 p1 = g.pointer1[0],
75294 l1 = g.pointer1[1],
75295 dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
75296 dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
75297 t = scale(t, Math.sqrt(dp / dl));
75298 p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
75299 l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
75300 } else if (g.pointer0) {
75305 g.zoom(d3_event, 'touch', constrain(translate(t, p, l), g.extent, translateExtent));
75308 function pointerup(d3_event) {
75309 if (!_downPointerIDs.has(d3_event.pointerId)) return;
75311 _downPointerIDs["delete"](d3_event.pointerId);
75313 if (!_activeGesture) return;
75314 var g = gesture(this, arguments);
75315 d3_event.stopImmediatePropagation();
75316 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;
75318 if (g.pointer1 && !g.pointer0) {
75319 g.pointer0 = g.pointer1;
75323 if (g.pointer0) g.pointer0[1] = _transform.invert(g.pointer0[0]);else {
75328 zoom.wheelDelta = function (_) {
75329 return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
75332 zoom.filter = function (_) {
75333 return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
75336 zoom.extent = function (_) {
75337 return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
75340 zoom.scaleExtent = function (_) {
75341 return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
75344 zoom.translateExtent = function (_) {
75345 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]]];
75348 zoom.constrain = function (_) {
75349 return arguments.length ? (constrain = _, zoom) : constrain;
75352 zoom.interpolate = function (_) {
75353 return arguments.length ? (interpolate = _, zoom) : interpolate;
75356 zoom._transform = function (_) {
75357 return arguments.length ? (_transform = _, zoom) : _transform;
75360 return utilRebind(zoom, dispatch$1, 'on');
75363 // if pointer events are supported. Falls back to default `dblclick` event.
75365 function utilDoubleUp() {
75366 var dispatch$1 = dispatch('doubleUp');
75367 var _maxTimespan = 500; // milliseconds
75369 var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
75371 var _pointer; // object representing the pointer that could trigger double up
75374 function pointerIsValidFor(loc) {
75375 // second pointerup must occur within a small timeframe after the first pointerdown
75376 return new Date().getTime() - _pointer.startTime <= _maxTimespan && // all pointer events must occur within a small distance of the first pointerdown
75377 geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
75380 function pointerdown(d3_event) {
75381 // ignore right-click
75382 if (d3_event.ctrlKey || d3_event.button === 2) return;
75383 var loc = [d3_event.clientX, d3_event.clientY]; // Don't rely on pointerId here since it can change between pointerdown
75384 // events on touch devices
75386 if (_pointer && !pointerIsValidFor(loc)) {
75387 // if this pointer is no longer valid, clear it so another can be started
75388 _pointer = undefined;
75394 startTime: new Date().getTime(),
75396 pointerId: d3_event.pointerId
75400 _pointer.pointerId = d3_event.pointerId;
75404 function pointerup(d3_event) {
75405 // ignore right-click
75406 if (d3_event.ctrlKey || d3_event.button === 2) return;
75407 if (!_pointer || _pointer.pointerId !== d3_event.pointerId) return;
75408 _pointer.upCount += 1;
75410 if (_pointer.upCount === 2) {
75412 var loc = [d3_event.clientX, d3_event.clientY];
75414 if (pointerIsValidFor(loc)) {
75415 var locInThis = utilFastMouse(this)(d3_event);
75416 dispatch$1.call('doubleUp', this, d3_event, locInThis);
75417 } // clear the pointer info in any case
75420 _pointer = undefined;
75424 function doubleUp(selection) {
75425 if ('PointerEvent' in window) {
75426 // dblclick isn't well supported on touch devices so manually use
75427 // pointer events if they're available
75428 selection.on('pointerdown.doubleUp', pointerdown).on('pointerup.doubleUp', pointerup);
75430 // fallback to dblclick
75431 selection.on('dblclick.doubleUp', function (d3_event) {
75432 dispatch$1.call('doubleUp', this, d3_event, utilFastMouse(this)(d3_event));
75437 doubleUp.off = function (selection) {
75438 selection.on('pointerdown.doubleUp', null).on('pointerup.doubleUp', null).on('dblclick.doubleUp', null);
75441 return utilRebind(doubleUp, dispatch$1, 'on');
75444 var TILESIZE = 256;
75447 var kMin = geoZoomToScale(minZoom, TILESIZE);
75448 var kMax = geoZoomToScale(maxZoom, TILESIZE);
75450 function clamp(num, min, max) {
75451 return Math.max(min, Math.min(num, max));
75454 function rendererMap(context) {
75455 var dispatch$1 = dispatch('move', 'drawn', 'crossEditableZoom', 'hitMinZoom', 'changeHighlighting', 'changeAreaFill');
75456 var projection = context.projection;
75457 var curtainProjection = context.curtainProjection;
75466 var _selection = select(null);
75468 var supersurface = select(null);
75469 var wrapper = select(null);
75470 var surface = select(null);
75471 var _dimensions = [1, 1];
75472 var _dblClickZoomEnabled = true;
75473 var _redrawEnabled = true;
75475 var _gestureTransformStart;
75477 var _transformStart = projection.transform();
75479 var _transformLast;
75481 var _isTransformed = false;
75484 var _getMouseCoords;
75486 var _lastPointerEvent;
75488 var _lastWithinEditableZoom; // whether a pointerdown event started the zoom
75491 var _pointerDown = false; // use pointer events on supported platforms; fallback to mouse events
75493 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse'; // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
75496 var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
75498 var _zoomerPanner = _zoomerPannerFunction().scaleExtent([kMin, kMax]).interpolate(interpolate).filter(zoomEventFilter).on('zoom.map', zoomPan).on('start.map', function (d3_event) {
75499 _pointerDown = d3_event && (d3_event.type === 'pointerdown' || d3_event.sourceEvent && d3_event.sourceEvent.type === 'pointerdown');
75500 }).on('end.map', function () {
75501 _pointerDown = false;
75504 var _doubleUpHandler = utilDoubleUp();
75506 var scheduleRedraw = throttle(redraw, 750); // var isRedrawScheduled = false;
75507 // var pendingRedrawCall;
75508 // function scheduleRedraw() {
75509 // // Only schedule the redraw if one has not already been set.
75510 // if (isRedrawScheduled) return;
75511 // isRedrawScheduled = true;
75512 // var that = this;
75513 // var args = arguments;
75514 // pendingRedrawCall = window.requestIdleCallback(function () {
75515 // // Reset the boolean so future redraws can be set.
75516 // isRedrawScheduled = false;
75517 // redraw.apply(that, args);
75518 // }, { timeout: 1400 });
75522 function cancelPendingRedraw() {
75523 scheduleRedraw.cancel(); // isRedrawScheduled = false;
75524 // window.cancelIdleCallback(pendingRedrawCall);
75527 function map(selection) {
75528 _selection = selection;
75529 context.on('change.map', immediateRedraw);
75530 var osm = context.connection();
75533 osm.on('change.map', immediateRedraw);
75536 function didUndoOrRedo(targetTransform) {
75537 var mode = context.mode().id;
75538 if (mode !== 'browse' && mode !== 'select') return;
75540 if (targetTransform) {
75541 map.transformEase(targetTransform);
75545 context.history().on('merge.map', function () {
75547 }).on('change.map', immediateRedraw).on('undone.map', function (stack, fromStack) {
75548 didUndoOrRedo(fromStack.transform);
75549 }).on('redone.map', function (stack) {
75550 didUndoOrRedo(stack.transform);
75552 context.background().on('change.map', immediateRedraw);
75553 context.features().on('redraw.map', immediateRedraw);
75554 drawLayers.on('change.map', function () {
75555 context.background().updateImagery();
75558 selection.on('wheel.map mousewheel.map', function (d3_event) {
75559 // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
75560 d3_event.preventDefault();
75561 }).call(_zoomerPanner).call(_zoomerPanner.transform, projection.transform()).on('dblclick.zoom', null); // override d3-zoom dblclick handling
75563 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
75564 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
75566 wrapper = supersurface.append('div').attr('class', 'layer layer-data');
75567 map.surface = surface = wrapper.call(drawLayers).selectAll('.surface');
75568 surface.call(drawLabels.observe).call(_doubleUpHandler).on(_pointerPrefix + 'down.zoom', function (d3_event) {
75569 _lastPointerEvent = d3_event;
75571 if (d3_event.button === 2) {
75572 d3_event.stopPropagation();
75574 }, true).on(_pointerPrefix + 'up.zoom', function (d3_event) {
75575 _lastPointerEvent = d3_event;
75577 if (resetTransform()) {
75580 }).on(_pointerPrefix + 'move.map', function (d3_event) {
75581 _lastPointerEvent = d3_event;
75582 }).on(_pointerPrefix + 'over.vertices', function (d3_event) {
75583 if (map.editableDataEnabled() && !_isTransformed) {
75584 var hover = d3_event.target.__data__;
75585 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
75586 dispatch$1.call('drawn', this, {
75590 }).on(_pointerPrefix + 'out.vertices', function (d3_event) {
75591 if (map.editableDataEnabled() && !_isTransformed) {
75592 var hover = d3_event.relatedTarget && d3_event.relatedTarget.__data__;
75593 surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
75594 dispatch$1.call('drawn', this, {
75599 var detected = utilDetect(); // only WebKit supports gesture events
75601 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
75602 // but we only need to do this on desktop Safari anyway. – #7694
75603 !detected.isMobileWebKit) {
75604 // Desktop Safari sends gesture events for multitouch trackpad pinches.
75605 // We can listen for these and translate them into map zooms.
75606 surface.on('gesturestart.surface', function (d3_event) {
75607 d3_event.preventDefault();
75608 _gestureTransformStart = projection.transform();
75609 }).on('gesturechange.surface', gestureChange);
75610 } // must call after surface init
75615 _doubleUpHandler.on('doubleUp.map', function (d3_event, p0) {
75616 if (!_dblClickZoomEnabled) return; // don't zoom if targeting something other than the map itself
75618 if (_typeof(d3_event.target.__data__) === 'object' && // or area fills
75619 !select(d3_event.target).classed('fill')) return;
75620 var zoomOut = d3_event.shiftKey;
75621 var t = projection.transform();
75622 var p1 = t.invert(p0);
75623 t = t.scale(zoomOut ? 0.5 : 2);
75624 t.x = p0[0] - p1[0] * t.k;
75625 t.y = p0[1] - p1[1] * t.k;
75626 map.transformEase(t);
75629 context.on('enter.map', function () {
75630 if (!map.editableDataEnabled(true
75631 /* skip zoom check */
75632 )) return; // redraw immediately any objects affected by a change in selectedIDs.
75634 var graph = context.graph();
75635 var selectedAndParents = {};
75636 context.selectedIDs().forEach(function (id) {
75637 var entity = graph.hasEntity(id);
75640 selectedAndParents[entity.id] = entity;
75642 if (entity.type === 'node') {
75643 graph.parentWays(entity).forEach(function (parent) {
75644 selectedAndParents[parent.id] = parent;
75649 var data = Object.values(selectedAndParents);
75651 var filter = function filter(d) {
75652 return d.id in selectedAndParents;
75655 data = context.features().filter(data, graph);
75656 surface.call(drawVertices.drawSelected, graph, map.extent()).call(drawLines, graph, data, filter).call(drawAreas, graph, data, filter).call(drawMidpoints, graph, data, filter, map.trimmedExtent());
75657 dispatch$1.call('drawn', this, {
75659 }); // redraw everything else later
75663 map.dimensions(utilGetDimensions(selection));
75666 function zoomEventFilter(d3_event) {
75667 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
75668 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
75669 // This can happen if a previous `mousedown` occurred without a `mouseup`.
75670 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
75671 // so that d3-zoom won't stop propagation of new `mousedown` events.
75672 if (d3_event.type === 'mousedown') {
75673 var hasOrphan = false;
75674 var listeners = window.__on;
75676 for (var i = 0; i < listeners.length; i++) {
75677 var listener = listeners[i];
75679 if (listener.name === 'zoom' && listener.type === 'mouseup') {
75686 var event = window.CustomEvent;
75689 event = new event('mouseup');
75691 event = window.document.createEvent('Event');
75692 event.initEvent('mouseup', false, false);
75693 } // Event needs to be dispatched with an event.view property.
75696 event.view = window;
75697 window.dispatchEvent(event);
75701 return d3_event.button !== 2; // ignore right clicks
75704 function pxCenter() {
75705 return [_dimensions[0] / 2, _dimensions[1] / 2];
75708 function drawEditable(difference, extent) {
75709 var mode = context.mode();
75710 var graph = context.graph();
75711 var features = context.features();
75712 var all = context.history().intersects(map.extent());
75713 var fullRedraw = false;
75717 var applyFeatureLayerFilters = true;
75719 if (map.isInWideSelection()) {
75721 utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function (id) {
75722 var entity = context.hasEntity(id);
75723 if (entity) data.push(entity);
75726 filter = utilFunctor(true); // selected features should always be visible, so we can skip filtering
75728 applyFeatureLayerFilters = false;
75729 } else if (difference) {
75730 var complete = difference.complete(map.extent());
75731 data = Object.values(complete).filter(Boolean);
75732 set = new Set(Object.keys(complete));
75734 filter = function filter(d) {
75735 return set.has(d.id);
75738 features.clear(data);
75740 // force a full redraw if gatherStats detects that a feature
75741 // should be auto-hidden (e.g. points or buildings)..
75742 if (features.gatherStats(all, graph, _dimensions)) {
75743 extent = undefined;
75747 data = context.history().intersects(map.extent().intersection(extent));
75748 set = new Set(data.map(function (entity) {
75752 filter = function filter(d) {
75753 return set.has(d.id);
75758 filter = utilFunctor(true);
75762 if (applyFeatureLayerFilters) {
75763 data = features.filter(data, graph);
75765 context.features().resetStats();
75768 if (mode && mode.id === 'select') {
75769 // update selected vertices - the user might have just double-clicked a way,
75770 // creating a new vertex, triggering a partial redraw without a mode change
75771 surface.call(drawVertices.drawSelected, graph, map.extent());
75774 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);
75775 dispatch$1.call('drawn', this, {
75780 map.init = function () {
75781 drawLayers = svgLayers(projection, context);
75782 drawPoints = svgPoints(projection, context);
75783 drawVertices = svgVertices(projection, context);
75784 drawLines = svgLines(projection, context);
75785 drawAreas = svgAreas(projection, context);
75786 drawMidpoints = svgMidpoints(projection, context);
75787 drawLabels = svgLabels(projection, context);
75790 function editOff() {
75791 context.features().resetStats();
75792 surface.selectAll('.layer-osm *').remove();
75793 surface.selectAll('.layer-touch:not(.markers) *').remove();
75797 'select-note': true,
75798 'select-data': true,
75799 'select-error': true
75801 var mode = context.mode();
75803 if (mode && !allowed[mode.id]) {
75804 context.enter(modeBrowse(context));
75807 dispatch$1.call('drawn', this, {
75812 function gestureChange(d3_event) {
75813 // Remap Safari gesture events to wheel events - #5492
75814 // We want these disabled most places, but enabled for zoom/unzoom on map surface
75815 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
75817 e.preventDefault();
75820 // dummy values to ignore in zoomPan
75822 // dummy values to ignore in zoomPan
75823 clientX: e.clientX,
75824 clientY: e.clientY,
75825 screenX: e.screenX,
75826 screenY: e.screenY,
75830 var e2 = new WheelEvent('wheel', props);
75831 e2._scale = e.scale; // preserve the original scale
75833 e2._rotation = e.rotation; // preserve the original rotation
75835 _selection.node().dispatchEvent(e2);
75838 function zoomPan(event, key, transform) {
75839 var source = event && event.sourceEvent || event;
75840 var eventTransform = transform || event && event.transform;
75841 var x = eventTransform.x;
75842 var y = eventTransform.y;
75843 var k = eventTransform.k; // Special handling of 'wheel' events:
75844 // They might be triggered by the user scrolling the mouse wheel,
75845 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
75847 if (source && source.type === 'wheel') {
75848 // assume that the gesture is already handled by pointer events
75849 if (_pointerDown) return;
75850 var detected = utilDetect();
75851 var dX = source.deltaX;
75852 var dY = source.deltaY;
75856 var t0, p0, p1; // Normalize mousewheel scroll speed (Firefox) - #3029
75857 // If wheel delta is provided in LINE units, recalculate it in PIXEL units
75858 // We are essentially redoing the calculations that occur here:
75859 // https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
75860 // See this for more info:
75861 // https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
75863 if (source.deltaMode === 1
75866 // Convert from lines to pixels, more if the user is scrolling fast.
75867 // (I made up the exp function to roughly match Firefox to what Chrome does)
75868 // These numbers should be floats, because integers are treated as pan gesture below.
75869 var lines = Math.abs(source.deltaY);
75870 var sign = source.deltaY > 0 ? 1 : -1;
75871 dY = sign * clamp(Math.exp((lines - 1) * 0.75) * 4.000244140625, 4.000244140625, // min
75872 350.000244140625 // max
75873 ); // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
75874 // There doesn't seem to be any scroll acceleration.
75875 // This multiplier increases the speed a little bit - #5512
75877 if (detected.os !== 'mac') {
75879 } // recalculate x2,y2,k2
75882 t0 = _isTransformed ? _transformLast : _transformStart;
75883 p0 = _getMouseCoords(source);
75884 p1 = t0.invert(p0);
75885 k2 = t0.k * Math.pow(2, -dY / 500);
75886 k2 = clamp(k2, kMin, kMax);
75887 x2 = p0[0] - p1[0] * k2;
75888 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (Safari) - #5492
75889 // These are fake `wheel` events we made from Safari `gesturechange` events..
75890 } else if (source._scale) {
75891 // recalculate x2,y2,k2
75892 t0 = _gestureTransformStart;
75893 p0 = _getMouseCoords(source);
75894 p1 = t0.invert(p0);
75895 k2 = t0.k * source._scale;
75896 k2 = clamp(k2, kMin, kMax);
75897 x2 = p0[0] - p1[0] * k2;
75898 y2 = p0[1] - p1[1] * k2; // 2 finger map pinch zooming (all browsers except Safari) - #5492
75899 // Pinch zooming via the `wheel` event will always have:
75900 // - `ctrlKey = true`
75901 // - `deltaY` is not round integer pixels (ignore `deltaX`)
75902 } else if (source.ctrlKey && !isInteger(dY)) {
75903 dY *= 6; // slightly scale up whatever the browser gave us
75904 // recalculate x2,y2,k2
75906 t0 = _isTransformed ? _transformLast : _transformStart;
75907 p0 = _getMouseCoords(source);
75908 p1 = t0.invert(p0);
75909 k2 = t0.k * Math.pow(2, -dY / 500);
75910 k2 = clamp(k2, kMin, kMax);
75911 x2 = p0[0] - p1[0] * k2;
75912 y2 = p0[1] - p1[1] * k2; // Trackpad scroll zooming with shift or alt/option key down
75913 } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
75914 // recalculate x2,y2,k2
75915 t0 = _isTransformed ? _transformLast : _transformStart;
75916 p0 = _getMouseCoords(source);
75917 p1 = t0.invert(p0);
75918 k2 = t0.k * Math.pow(2, -dY / 500);
75919 k2 = clamp(k2, kMin, kMax);
75920 x2 = p0[0] - p1[0] * k2;
75921 y2 = p0[1] - p1[1] * k2; // 2 finger map panning (Mac only, all browsers) - #5492, #5512
75922 // Panning via the `wheel` event will always have:
75923 // - `ctrlKey = false`
75924 // - `deltaX`,`deltaY` are round integer pixels
75925 } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
75926 p1 = projection.translate();
75929 k2 = projection.scale();
75930 k2 = clamp(k2, kMin, kMax);
75931 } // something changed - replace the event transform
75934 if (x2 !== x || y2 !== y || k2 !== k) {
75938 eventTransform = identity$2.translate(x2, y2).scale(k2);
75940 if (_zoomerPanner._transform) {
75941 // utilZoomPan interface
75942 _zoomerPanner._transform(eventTransform);
75944 // d3_zoom interface
75945 _selection.node().__zoom = eventTransform;
75950 if (_transformStart.x === x && _transformStart.y === y && _transformStart.k === k) {
75951 return; // no change
75954 var withinEditableZoom = map.withinEditableZoom();
75956 if (_lastWithinEditableZoom !== withinEditableZoom) {
75957 if (_lastWithinEditableZoom !== undefined) {
75958 // notify that the map zoomed in or out over the editable zoom threshold
75959 dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
75962 _lastWithinEditableZoom = withinEditableZoom;
75965 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
75966 surface.interrupt();
75967 dispatch$1.call('hitMinZoom', this, map);
75968 setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
75970 dispatch$1.call('move', this, map);
75974 projection.transform(eventTransform);
75975 var scale = k / _transformStart.k;
75976 var tX = (x / scale - _transformStart.x) * scale;
75977 var tY = (y / scale - _transformStart.y) * scale;
75979 if (context.inIntro()) {
75980 curtainProjection.transform({
75988 _lastPointerEvent = event;
75991 _isTransformed = true;
75992 _transformLast = eventTransform;
75993 utilSetTransform(supersurface, tX, tY, scale);
75995 dispatch$1.call('move', this, map);
75997 function isInteger(val) {
75998 return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
76002 function resetTransform() {
76003 if (!_isTransformed) return false;
76004 utilSetTransform(supersurface, 0, 0);
76005 _isTransformed = false;
76007 if (context.inIntro()) {
76008 curtainProjection.transform(projection.transform());
76014 function redraw(difference, extent) {
76015 if (surface.empty() || !_redrawEnabled) return; // If we are in the middle of a zoom/pan, we can't do differenced redraws.
76016 // It would result in artifacts where differenced entities are redrawn with
76017 // one transform and unchanged entities with another.
76019 if (resetTransform()) {
76020 difference = extent = undefined;
76023 var zoom = map.zoom();
76024 var z = String(~~zoom);
76026 if (surface.attr('data-zoom') !== z) {
76027 surface.attr('data-zoom', z);
76028 } // class surface as `lowzoom` around z17-z18.5 (based on latitude)
76031 var lat = map.center()[1];
76032 var lowzoom = linear$2().domain([-60, 0, 60]).range([17, 18.5, 17]).clamp(true);
76033 surface.classed('low-zoom', zoom <= lowzoom(lat));
76036 supersurface.call(context.background());
76037 wrapper.call(drawLayers);
76041 if (map.editableDataEnabled() || map.isInWideSelection()) {
76042 context.loadTiles(projection);
76043 drawEditable(difference, extent);
76048 _transformStart = projection.transform();
76052 var immediateRedraw = function immediateRedraw(difference, extent) {
76053 if (!difference && !extent) cancelPendingRedraw();
76054 redraw(difference, extent);
76057 map.lastPointerEvent = function () {
76058 return _lastPointerEvent;
76061 map.mouse = function (d3_event) {
76062 var event = _lastPointerEvent || d3_event;
76067 while (s = event.sourceEvent) {
76071 return _getMouseCoords(event);
76075 }; // returns Lng/Lat
76078 map.mouseCoordinates = function () {
76079 var coord = map.mouse() || pxCenter();
76080 return projection.invert(coord);
76083 map.dblclickZoomEnable = function (val) {
76084 if (!arguments.length) return _dblClickZoomEnabled;
76085 _dblClickZoomEnabled = val;
76089 map.redrawEnable = function (val) {
76090 if (!arguments.length) return _redrawEnabled;
76091 _redrawEnabled = val;
76095 map.isTransformed = function () {
76096 return _isTransformed;
76099 function setTransform(t2, duration, force) {
76100 var t = projection.transform();
76101 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) return false;
76104 _selection.transition().duration(duration).on('start', function () {
76106 }).call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
76108 projection.transform(t2);
76109 _transformStart = t2;
76111 _selection.call(_zoomerPanner.transform, _transformStart);
76117 function setCenterZoom(loc2, z2, duration, force) {
76118 var c = map.center();
76119 var z = map.zoom();
76120 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) return false;
76121 var proj = geoRawMercator().transform(projection.transform()); // copy projection
76123 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
76125 var t = proj.translate();
76126 var point = proj(loc2);
76127 var center = pxCenter();
76128 t[0] += center[0] - point[0];
76129 t[1] += center[1] - point[1];
76130 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
76133 map.pan = function (delta, duration) {
76134 var t = projection.translate();
76135 var k = projection.scale();
76140 _selection.transition().duration(duration).on('start', function () {
76142 }).call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
76144 projection.translate(t);
76145 _transformStart = projection.transform();
76147 _selection.call(_zoomerPanner.transform, _transformStart);
76149 dispatch$1.call('move', this, map);
76156 map.dimensions = function (val) {
76157 if (!arguments.length) return _dimensions;
76159 drawLayers.dimensions(_dimensions);
76160 context.background().dimensions(_dimensions);
76161 projection.clipExtent([[0, 0], _dimensions]);
76162 _getMouseCoords = utilFastMouse(supersurface.node());
76167 function zoomIn(delta) {
76168 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
76171 function zoomOut(delta) {
76172 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
76175 map.zoomIn = function () {
76179 map.zoomInFurther = function () {
76183 map.canZoomIn = function () {
76184 return map.zoom() < maxZoom;
76187 map.zoomOut = function () {
76191 map.zoomOutFurther = function () {
76195 map.canZoomOut = function () {
76196 return map.zoom() > minZoom;
76199 map.center = function (loc2) {
76200 if (!arguments.length) {
76201 return projection.invert(pxCenter());
76204 if (setCenterZoom(loc2, map.zoom())) {
76205 dispatch$1.call('move', this, map);
76212 map.unobscuredCenterZoomEase = function (loc, zoom) {
76213 var offset = map.unobscuredOffsetPx();
76214 var proj = geoRawMercator().transform(projection.transform()); // copy projection
76215 // use the target zoom to calculate the offset center
76217 proj.scale(geoZoomToScale(zoom, TILESIZE));
76218 var locPx = proj(loc);
76219 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
76220 var offsetLoc = proj.invert(offsetLocPx);
76221 map.centerZoomEase(offsetLoc, zoom);
76224 map.unobscuredOffsetPx = function () {
76225 var openPane = context.container().select('.map-panes .map-pane.shown');
76227 if (!openPane.empty()) {
76228 return [openPane.node().offsetWidth / 2, 0];
76234 map.zoom = function (z2) {
76235 if (!arguments.length) {
76236 return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
76239 if (z2 < _minzoom) {
76240 surface.interrupt();
76241 dispatch$1.call('hitMinZoom', this, map);
76242 z2 = context.minEditableZoom();
76245 if (setCenterZoom(map.center(), z2)) {
76246 dispatch$1.call('move', this, map);
76253 map.centerZoom = function (loc2, z2) {
76254 if (setCenterZoom(loc2, z2)) {
76255 dispatch$1.call('move', this, map);
76262 map.zoomTo = function (entity) {
76263 var extent = entity.extent(context.graph());
76264 if (!isFinite(extent.area())) return map;
76265 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
76266 return map.centerZoom(extent.center(), z2);
76269 map.centerEase = function (loc2, duration) {
76270 duration = duration || 250;
76271 setCenterZoom(loc2, map.zoom(), duration);
76275 map.zoomEase = function (z2, duration) {
76276 duration = duration || 250;
76277 setCenterZoom(map.center(), z2, duration, false);
76281 map.centerZoomEase = function (loc2, z2, duration) {
76282 duration = duration || 250;
76283 setCenterZoom(loc2, z2, duration, false);
76287 map.transformEase = function (t2, duration) {
76288 duration = duration || 250;
76289 setTransform(t2, duration, false
76295 map.zoomToEase = function (obj, duration) {
76298 if (Array.isArray(obj)) {
76299 obj.forEach(function (entity) {
76300 var entityExtent = entity.extent(context.graph());
76303 extent = entityExtent;
76305 extent = extent.extend(entityExtent);
76309 extent = obj.extent(context.graph());
76312 if (!isFinite(extent.area())) return map;
76313 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
76314 return map.centerZoomEase(extent.center(), z2, duration);
76317 map.startEase = function () {
76318 utilBindOnce(surface, _pointerPrefix + 'down.ease', function () {
76324 map.cancelEase = function () {
76325 _selection.interrupt();
76330 map.extent = function (val) {
76331 if (!arguments.length) {
76332 return new geoExtent(projection.invert([0, _dimensions[1]]), projection.invert([_dimensions[0], 0]));
76334 var extent = geoExtent(val);
76335 map.centerZoom(extent.center(), map.extentZoom(extent));
76339 map.trimmedExtent = function (val) {
76340 if (!arguments.length) {
76344 return new geoExtent(projection.invert([pad, _dimensions[1] - footerY - pad]), projection.invert([_dimensions[0] - pad, headerY + pad]));
76346 var extent = geoExtent(val);
76347 map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
76351 function calcExtentZoom(extent, dim) {
76352 var tl = projection([extent[0][0], extent[1][1]]);
76353 var br = projection([extent[1][0], extent[0][1]]); // Calculate maximum zoom that fits extent
76355 var hFactor = (br[0] - tl[0]) / dim[0];
76356 var vFactor = (br[1] - tl[1]) / dim[1];
76357 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
76358 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
76359 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
76363 map.extentZoom = function (val) {
76364 return calcExtentZoom(geoExtent(val), _dimensions);
76367 map.trimmedExtentZoom = function (val) {
76370 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
76371 return calcExtentZoom(geoExtent(val), trimmed);
76374 map.withinEditableZoom = function () {
76375 return map.zoom() >= context.minEditableZoom();
76378 map.isInWideSelection = function () {
76379 return !map.withinEditableZoom() && context.selectedIDs().length;
76382 map.editableDataEnabled = function (skipZoomCheck) {
76383 var layer = context.layers().layer('osm');
76384 if (!layer || !layer.enabled()) return false;
76385 return skipZoomCheck || map.withinEditableZoom();
76388 map.notesEditable = function () {
76389 var layer = context.layers().layer('notes');
76390 if (!layer || !layer.enabled()) return false;
76391 return map.withinEditableZoom();
76394 map.minzoom = function (val) {
76395 if (!arguments.length) return _minzoom;
76400 map.toggleHighlightEdited = function () {
76401 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
76402 map.pan([0, 0]); // trigger a redraw
76404 dispatch$1.call('changeHighlighting', this);
76407 map.areaFillOptions = ['wireframe', 'partial', 'full'];
76409 map.activeAreaFill = function (val) {
76410 if (!arguments.length) return corePreferences('area-fill') || 'partial';
76411 corePreferences('area-fill', val);
76413 if (val !== 'wireframe') {
76414 corePreferences('area-fill-toggle', val);
76418 map.pan([0, 0]); // trigger a redraw
76420 dispatch$1.call('changeAreaFill', this);
76424 map.toggleWireframe = function () {
76425 var activeFill = map.activeAreaFill();
76427 if (activeFill === 'wireframe') {
76428 activeFill = corePreferences('area-fill-toggle') || 'partial';
76430 activeFill = 'wireframe';
76433 map.activeAreaFill(activeFill);
76436 function updateAreaFill() {
76437 var activeFill = map.activeAreaFill();
76438 map.areaFillOptions.forEach(function (opt) {
76439 surface.classed('fill-' + opt, Boolean(opt === activeFill));
76443 map.layers = function () {
76447 map.doubleUpHandler = function () {
76448 return _doubleUpHandler;
76451 return utilRebind(map, dispatch$1, 'on');
76454 function rendererPhotos(context) {
76455 var dispatch$1 = dispatch('change');
76456 var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
76457 var _allPhotoTypes = ['flat', 'panoramic'];
76459 var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
76462 var _dateFilters = ['fromDate', 'toDate'];
76470 function photos() {}
76472 function updateStorage() {
76473 if (window.mocha) return;
76474 var hash = utilStringQs(window.location.hash);
76475 var enabled = context.layers().all().filter(function (d) {
76476 return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
76477 }).map(function (d) {
76481 if (enabled.length) {
76482 hash.photo_overlay = enabled.join(',');
76484 delete hash.photo_overlay;
76487 window.location.replace('#' + utilQsString(hash, true));
76490 photos.overlayLayerIDs = function () {
76494 photos.allPhotoTypes = function () {
76495 return _allPhotoTypes;
76498 photos.dateFilters = function () {
76499 return _dateFilters;
76502 photos.dateFilterValue = function (val) {
76503 return val === _dateFilters[0] ? _fromDate : _toDate;
76506 photos.setDateFilter = function (type, val, updateUrl) {
76507 // validate the date
76508 var date = val && new Date(val);
76510 if (date && !isNaN(date)) {
76511 val = date.toISOString().substr(0, 10);
76516 if (type === _dateFilters[0]) {
76519 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
76520 _toDate = _fromDate;
76524 if (type === _dateFilters[1]) {
76527 if (_fromDate && _toDate && new Date(_toDate) < new Date(_fromDate)) {
76528 _fromDate = _toDate;
76532 dispatch$1.call('change', this);
76537 if (_fromDate || _toDate) {
76538 rangeString = (_fromDate || '') + '_' + (_toDate || '');
76541 setUrlFilterValue('photo_dates', rangeString);
76545 photos.setUsernameFilter = function (val, updateUrl) {
76546 if (val && typeof val === 'string') val = val.replace(/;/g, ',').split(',');
76549 val = val.map(function (d) {
76551 }).filter(Boolean);
76559 dispatch$1.call('change', this);
76565 hashString = _usernames.join(',');
76568 setUrlFilterValue('photo_username', hashString);
76572 function setUrlFilterValue(property, val) {
76573 if (!window.mocha) {
76574 var hash = utilStringQs(window.location.hash);
76577 if (hash[property] === val) return;
76578 hash[property] = val;
76580 if (!(property in hash)) return;
76581 delete hash[property];
76584 window.location.replace('#' + utilQsString(hash, true));
76588 function showsLayer(id) {
76589 var layer = context.layers().layer(id);
76590 return layer && layer.supported() && layer.enabled();
76593 photos.shouldFilterByDate = function () {
76594 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
76597 photos.shouldFilterByPhotoType = function () {
76598 return showsLayer('mapillary') || showsLayer('streetside') && showsLayer('openstreetcam');
76601 photos.shouldFilterByUsername = function () {
76602 return showsLayer('mapillary') || showsLayer('openstreetcam') || showsLayer('streetside');
76605 photos.showsPhotoType = function (val) {
76606 if (!photos.shouldFilterByPhotoType()) return true;
76607 return _shownPhotoTypes.indexOf(val) !== -1;
76610 photos.showsFlat = function () {
76611 return photos.showsPhotoType('flat');
76614 photos.showsPanoramic = function () {
76615 return photos.showsPhotoType('panoramic');
76618 photos.fromDate = function () {
76622 photos.toDate = function () {
76626 photos.togglePhotoType = function (val) {
76627 var index = _shownPhotoTypes.indexOf(val);
76629 if (index !== -1) {
76630 _shownPhotoTypes.splice(index, 1);
76632 _shownPhotoTypes.push(val);
76635 dispatch$1.call('change', this);
76639 photos.usernames = function () {
76643 photos.init = function () {
76644 var hash = utilStringQs(window.location.hash);
76646 if (hash.photo_dates) {
76647 // expect format like `photo_dates=2019-01-01_2020-12-31`, but allow a couple different separators
76648 var parts = /^(.*)[–_](.*)$/g.exec(hash.photo_dates.trim());
76649 this.setDateFilter('fromDate', parts && parts.length >= 2 && parts[1], false);
76650 this.setDateFilter('toDate', parts && parts.length >= 3 && parts[2], false);
76653 if (hash.photo_username) {
76654 this.setUsernameFilter(hash.photo_username, false);
76657 if (hash.photo_overlay) {
76658 // support enabling photo layers by default via a URL parameter, e.g. `photo_overlay=openstreetcam;mapillary;streetside`
76659 var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
76660 hashOverlayIDs.forEach(function (id) {
76661 var layer = _layerIDs.indexOf(id) !== -1 && context.layers().layer(id);
76662 if (layer && !layer.enabled()) layer.enabled(true);
76667 // support opening a photo via a URL parameter, e.g. `photo=mapillary-fztgSDtLpa08ohPZFZjeRQ`
76668 var photoIds = hash.photo.replace(/;/g, ',').split(',');
76669 var photoId = photoIds.length && photoIds[0].trim();
76670 var results = /(.*)\/(.*)/g.exec(photoId);
76672 if (results && results.length >= 3) {
76673 var serviceId = results[1];
76674 var photoKey = results[2];
76675 var service = services[serviceId];
76677 if (service && service.ensureViewerLoaded) {
76678 // if we're showing a photo then make sure its layer is enabled too
76679 var layer = _layerIDs.indexOf(serviceId) !== -1 && context.layers().layer(serviceId);
76680 if (layer && !layer.enabled()) layer.enabled(true);
76681 var baselineTime = Date.now();
76682 service.on('loadedImages.rendererPhotos', function () {
76683 // don't open the viewer if too much time has elapsed
76684 if (Date.now() - baselineTime > 45000) {
76685 service.on('loadedImages.rendererPhotos', null);
76689 if (!service.cachedImage(photoKey)) return;
76690 service.on('loadedImages.rendererPhotos', null);
76691 service.ensureViewerLoaded(context).then(function () {
76692 service.selectImage(context, photoKey).showViewer(context);
76699 context.layers().on('change.rendererPhotos', updateStorage);
76702 return utilRebind(photos, dispatch$1, 'on');
76705 function uiAccount(context) {
76706 var osm = context.connection();
76708 function update(selection) {
76711 if (!osm.authenticated()) {
76712 selection.selectAll('.userLink, .logoutLink').classed('hide', true);
76716 osm.userDetails(function (err, details) {
76717 var userLink = selection.select('.userLink'),
76718 logoutLink = selection.select('.logoutLink');
76720 logoutLink.html('');
76721 if (err || !details) return;
76722 selection.selectAll('.userLink, .logoutLink').classed('hide', false); // Link
76724 var userLinkA = userLink.append('a').attr('href', osm.userURL(details.display_name)).attr('target', '_blank'); // Add thumbnail or dont
76726 if (details.image_url) {
76727 userLinkA.append('img').attr('class', 'icon pre-text user-icon').attr('src', details.image_url);
76729 userLinkA.call(svgIcon('#iD-icon-avatar', 'pre-text light'));
76733 userLinkA.append('span').attr('class', 'label').html(details.display_name);
76734 logoutLink.append('a').attr('class', 'logout').attr('href', '#').html(_t.html('logout')).on('click.logout', function (d3_event) {
76735 d3_event.preventDefault();
76741 return function (selection) {
76742 selection.append('li').attr('class', 'userLink').classed('hide', true);
76743 selection.append('li').attr('class', 'logoutLink').classed('hide', true);
76746 osm.on('change.account', function () {
76754 function uiAttribution(context) {
76755 var _selection = select(null);
76757 function render(selection, data, klass) {
76758 var div = selection.selectAll(".".concat(klass)).data([0]);
76759 div = div.enter().append('div').attr('class', klass).merge(div);
76760 var attributions = div.selectAll('.attribution').data(data, function (d) {
76763 attributions.exit().remove();
76764 attributions = attributions.enter().append('span').attr('class', 'attribution').each(function (d, i, nodes) {
76765 var attribution = select(nodes[i]);
76767 if (d.terms_html) {
76768 attribution.html(d.terms_html);
76773 attribution = attribution.append('a').attr('href', d.terms_url).attr('target', '_blank');
76776 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
76777 var terms_text = _t("imagery.".concat(sourceID, ".attribution.text"), {
76778 "default": d.terms_text || d.id || d.name()
76781 if (d.icon && !d.overlay) {
76782 attribution.append('img').attr('class', 'source-image').attr('src', d.icon);
76785 attribution.append('span').attr('class', 'attribution-text').html(terms_text);
76786 }).merge(attributions);
76787 var copyright = attributions.selectAll('.copyright-notice').data(function (d) {
76788 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
76789 return notice ? [notice] : [];
76791 copyright.exit().remove();
76792 copyright = copyright.enter().append('span').attr('class', 'copyright-notice').merge(copyright);
76793 copyright.html(String);
76796 function update() {
76797 var baselayer = context.background().baseLayerSource();
76799 _selection.call(render, baselayer ? [baselayer] : [], 'base-layer-attribution');
76801 var z = context.map().zoom();
76802 var overlays = context.background().overlayLayerSources() || [];
76804 _selection.call(render, overlays.filter(function (s) {
76805 return s.validZoom(z);
76806 }), 'overlay-layer-attribution');
76809 return function (selection) {
76810 _selection = selection;
76811 context.background().on('change.attribution', update);
76812 context.map().on('move.attribution', throttle(update, 400, {
76819 function uiContributors(context) {
76820 var osm = context.connection(),
76821 debouncedUpdate = debounce(function () {
76826 wrap = select(null);
76828 function update() {
76831 entities = context.history().intersects(context.map().extent());
76832 entities.forEach(function (entity) {
76833 if (entity && entity.user) users[entity.user] = true;
76835 var u = Object.keys(users),
76836 subset = u.slice(0, u.length > limit ? limit - 1 : limit);
76837 wrap.html('').call(svgIcon('#iD-icon-nearby', 'pre-text light'));
76838 var userList = select(document.createElement('span'));
76839 userList.selectAll().data(subset).enter().append('a').attr('class', 'user-link').attr('href', function (d) {
76840 return osm.userURL(d);
76841 }).attr('target', '_blank').html(String);
76843 if (u.length > limit) {
76844 var count = select(document.createElement('span'));
76845 var othersNum = u.length - limit + 1;
76846 count.append('a').attr('target', '_blank').attr('href', function () {
76847 return osm.changesetsURL(context.map().center(), context.map().zoom());
76848 }).html(othersNum);
76849 wrap.append('span').html(_t.html('contributors.truncated_list', {
76851 users: userList.html(),
76852 count: count.html()
76855 wrap.append('span').html(_t.html('contributors.list', {
76856 users: userList.html()
76862 wrap.transition().style('opacity', 0);
76863 } else if (hidden) {
76864 wrap.transition().style('opacity', 1);
76868 return function (selection) {
76872 osm.on('loaded.contributors', debouncedUpdate);
76873 context.map().on('move.contributors', debouncedUpdate);
76877 var _popoverID = 0;
76878 function uiPopover(klass) {
76879 var _id = _popoverID++;
76881 var _anchorSelection = select(null);
76883 var popover = function popover(selection) {
76884 _anchorSelection = selection;
76885 selection.each(setup);
76888 var _animation = utilFunctor(false);
76890 var _placement = utilFunctor('top'); // top, bottom, left, right
76893 var _alignment = utilFunctor('center'); // leading, center, trailing
76896 var _scrollContainer = utilFunctor(select(null));
76900 var _displayType = utilFunctor('');
76902 var _hasArrow = utilFunctor(true); // use pointer events on supported platforms; fallback to mouse events
76905 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
76907 popover.displayType = function (val) {
76908 if (arguments.length) {
76909 _displayType = utilFunctor(val);
76912 return _displayType;
76916 popover.hasArrow = function (val) {
76917 if (arguments.length) {
76918 _hasArrow = utilFunctor(val);
76925 popover.placement = function (val) {
76926 if (arguments.length) {
76927 _placement = utilFunctor(val);
76934 popover.alignment = function (val) {
76935 if (arguments.length) {
76936 _alignment = utilFunctor(val);
76943 popover.scrollContainer = function (val) {
76944 if (arguments.length) {
76945 _scrollContainer = utilFunctor(val);
76948 return _scrollContainer;
76952 popover.content = function (val) {
76953 if (arguments.length) {
76961 popover.isShown = function () {
76962 var popoverSelection = _anchorSelection.select('.popover-' + _id);
76964 return !popoverSelection.empty() && popoverSelection.classed('in');
76967 popover.show = function () {
76968 _anchorSelection.each(show);
76971 popover.updateContent = function () {
76972 _anchorSelection.each(updateContent);
76975 popover.hide = function () {
76976 _anchorSelection.each(hide);
76979 popover.toggle = function () {
76980 _anchorSelection.each(toggle);
76983 popover.destroy = function (selection, selector) {
76984 // by default, just destroy the current popover
76985 selector = selector || '.popover-' + _id;
76986 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 () {
76987 return this.getAttribute('data-original-title') || this.getAttribute('title');
76988 }).attr('data-original-title', null).selectAll(selector).remove();
76991 popover.destroyAny = function (selection) {
76992 selection.call(popover.destroy, '.popover');
76996 var anchor = select(this);
76998 var animate = _animation.apply(this, arguments);
77000 var popoverSelection = anchor.selectAll('.popover-' + _id).data([0]);
77001 var enter = popoverSelection.enter().append('div').attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : '')).classed('arrowed', _hasArrow.apply(this, arguments));
77002 enter.append('div').attr('class', 'popover-arrow');
77003 enter.append('div').attr('class', 'popover-inner');
77004 popoverSelection = enter.merge(popoverSelection);
77007 popoverSelection.classed('fade', true);
77010 var display = _displayType.apply(this, arguments);
77012 if (display === 'hover') {
77013 var _lastNonMouseEnterTime;
77015 anchor.on(_pointerPrefix + 'enter.popover', function (d3_event) {
77016 if (d3_event.pointerType) {
77017 if (d3_event.pointerType !== 'mouse') {
77018 _lastNonMouseEnterTime = d3_event.timeStamp; // only allow hover behavior for mouse input
77021 } else if (_lastNonMouseEnterTime && d3_event.timeStamp - _lastNonMouseEnterTime < 1500) {
77022 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
77023 // event for non-mouse interactions right after sending
77024 // the correct type pointerenter event. Workaround by discarding
77025 // any mouse event that occurs immediately after a non-mouse event.
77028 } // don't show if buttons are pressed, e.g. during click and drag of map
77031 if (d3_event.buttons !== 0) return;
77032 show.apply(this, arguments);
77033 }).on(_pointerPrefix + 'leave.popover', function () {
77034 hide.apply(this, arguments);
77035 }) // show on focus too for better keyboard navigation support
77036 .on('focus.popover', function () {
77037 show.apply(this, arguments);
77038 }).on('blur.popover', function () {
77039 hide.apply(this, arguments);
77041 } else if (display === 'clickFocus') {
77042 anchor.on(_pointerPrefix + 'down.popover', function (d3_event) {
77043 d3_event.preventDefault();
77044 d3_event.stopPropagation();
77045 }).on(_pointerPrefix + 'up.popover', function (d3_event) {
77046 d3_event.preventDefault();
77047 d3_event.stopPropagation();
77048 }).on('click.popover', toggle);
77049 popoverSelection // This attribute lets the popover take focus
77050 .attr('tabindex', 0).on('blur.popover', function () {
77051 anchor.each(function () {
77052 hide.apply(this, arguments);
77059 var anchor = select(this);
77060 var popoverSelection = anchor.selectAll('.popover-' + _id);
77062 if (popoverSelection.empty()) {
77063 // popover was removed somehow, put it back
77064 anchor.call(popover.destroy);
77065 anchor.each(setup);
77066 popoverSelection = anchor.selectAll('.popover-' + _id);
77069 popoverSelection.classed('in', true);
77071 var displayType = _displayType.apply(this, arguments);
77073 if (displayType === 'clickFocus') {
77074 anchor.classed('active', true);
77075 popoverSelection.node().focus();
77078 anchor.each(updateContent);
77081 function updateContent() {
77082 var anchor = select(this);
77085 anchor.selectAll('.popover-' + _id + ' > .popover-inner').call(_content.apply(this, arguments));
77088 updatePosition.apply(this, arguments); // hack: update multiple times to fix instances where the absolute offset is
77089 // set before the dynamic popover size is calculated by the browser
77091 updatePosition.apply(this, arguments);
77092 updatePosition.apply(this, arguments);
77095 function updatePosition() {
77096 var anchor = select(this);
77097 var popoverSelection = anchor.selectAll('.popover-' + _id);
77099 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
77101 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
77102 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
77103 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
77105 var placement = _placement.apply(this, arguments);
77107 popoverSelection.classed('left', false).classed('right', false).classed('top', false).classed('bottom', false).classed(placement, true);
77109 var alignment = _alignment.apply(this, arguments);
77111 var alignFactor = 0.5;
77113 if (alignment === 'leading') {
77115 } else if (alignment === 'trailing') {
77119 var anchorFrame = getFrame(anchor.node());
77120 var popoverFrame = getFrame(popoverSelection.node());
77123 switch (placement) {
77126 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
77127 y: anchorFrame.y - popoverFrame.h
77133 x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
77134 y: anchorFrame.y + anchorFrame.h
77140 x: anchorFrame.x - popoverFrame.w,
77141 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
77147 x: anchorFrame.x + anchorFrame.w,
77148 y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
77154 if (scrollNode && (placement === 'top' || placement === 'bottom')) {
77155 var initialPosX = position.x;
77157 if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
77158 position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
77159 } else if (position.x < 10) {
77163 var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow'); // keep the arrow centered on the button, or as close as possible
77165 var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
77166 arrow.style('left', ~~arrowPosX + 'px');
77169 popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
77171 popoverSelection.style('left', null).style('top', null);
77174 function getFrame(node) {
77175 var positionStyle = select(node).style('position');
77177 if (positionStyle === 'absolute' || positionStyle === 'static') {
77179 x: node.offsetLeft - scrollLeft,
77180 y: node.offsetTop - scrollTop,
77181 w: node.offsetWidth,
77182 h: node.offsetHeight
77188 w: node.offsetWidth,
77189 h: node.offsetHeight
77196 var anchor = select(this);
77198 if (_displayType.apply(this, arguments) === 'clickFocus') {
77199 anchor.classed('active', false);
77202 anchor.selectAll('.popover-' + _id).classed('in', false);
77205 function toggle() {
77206 if (select(this).select('.popover-' + _id).classed('in')) {
77207 hide.apply(this, arguments);
77209 show.apply(this, arguments);
77216 function uiTooltip(klass) {
77217 var tooltip = uiPopover((klass || '') + ' tooltip').displayType('hover');
77219 var _title = function _title() {
77220 var title = this.getAttribute('data-original-title');
77225 title = this.getAttribute('title');
77226 this.removeAttribute('title');
77227 this.setAttribute('data-original-title', title);
77233 var _heading = utilFunctor(null);
77235 var _keys = utilFunctor(null);
77237 tooltip.title = function (val) {
77238 if (!arguments.length) return _title;
77239 _title = utilFunctor(val);
77243 tooltip.heading = function (val) {
77244 if (!arguments.length) return _heading;
77245 _heading = utilFunctor(val);
77249 tooltip.keys = function (val) {
77250 if (!arguments.length) return _keys;
77251 _keys = utilFunctor(val);
77255 tooltip.content(function () {
77256 var heading = _heading.apply(this, arguments);
77258 var text = _title.apply(this, arguments);
77260 var keys = _keys.apply(this, arguments);
77262 return function (selection) {
77263 var headingSelect = selection.selectAll('.tooltip-heading').data(heading ? [heading] : []);
77264 headingSelect.exit().remove();
77265 headingSelect.enter().append('div').attr('class', 'tooltip-heading').merge(headingSelect).html(heading);
77266 var textSelect = selection.selectAll('.tooltip-text').data(text ? [text] : []);
77267 textSelect.exit().remove();
77268 textSelect.enter().append('div').attr('class', 'tooltip-text').merge(textSelect).html(text);
77269 var keyhintWrap = selection.selectAll('.keyhint-wrap').data(keys && keys.length ? [0] : []);
77270 keyhintWrap.exit().remove();
77271 var keyhintWrapEnter = keyhintWrap.enter().append('div').attr('class', 'keyhint-wrap');
77272 keyhintWrapEnter.append('span').html(_t.html('tooltip_keyhint'));
77273 keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
77274 keyhintWrap.selectAll('kbd.shortcut').data(keys && keys.length ? keys : []).enter().append('kbd').attr('class', 'shortcut').html(function (d) {
77282 function uiEditMenu(context) {
77283 var dispatch$1 = dispatch('toggled');
77285 var _menu = select(null);
77287 var _operations = []; // the position the menu should be displayed relative to
77289 var _anchorLoc = [0, 0];
77290 var _anchorLocLonLat = [0, 0]; // a string indicating how the menu was opened
77292 var _triggerType = '';
77293 var _vpTopMargin = 85; // viewport top margin
77295 var _vpBottomMargin = 45; // viewport bottom margin
77297 var _vpSideMargin = 35; // viewport side margin
77299 var _menuTop = false;
77303 var _menuWidth; // hardcode these values to make menu positioning easier
77306 var _verticalPadding = 4; // see also `.edit-menu .tooltip` CSS; include margin
77308 var _tooltipWidth = 210; // offset the menu slightly from the target location
77310 var _menuSideMargin = 10;
77311 var _tooltips = [];
77313 var editMenu = function editMenu(selection) {
77314 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
77316 var ops = _operations.filter(function (op) {
77317 return !isTouchMenu || !op.mouseOnly;
77320 if (!ops.length) return;
77321 _tooltips = []; // Position the menu above the anchor for stylus and finger input
77322 // since the mapper's hand likely obscures the screen below the anchor
77324 _menuTop = isTouchMenu; // Show labels for touch input since there aren't hover tooltips
77326 var showLabels = isTouchMenu;
77327 var buttonHeight = showLabels ? 32 : 34;
77330 // Get a general idea of the width based on the length of the label
77331 _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function (op) {
77332 return op.title.length;
77338 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
77339 _menu = selection.append('div').attr('class', 'edit-menu').classed('touch-menu', isTouchMenu).style('padding', _verticalPadding + 'px 0');
77341 var buttons = _menu.selectAll('.edit-menu-item').data(ops); // enter
77344 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
77345 return 'edit-menu-item edit-menu-item-' + d.id;
77346 }).style('height', buttonHeight + 'px').on('click', click) // don't listen for `mouseup` because we only care about non-mouse pointer types
77347 .on('pointerup', pointerup).on('pointerdown mousedown', function pointerdown(d3_event) {
77348 // don't let button presses also act as map input - #1869
77349 d3_event.stopPropagation();
77350 }).on('mouseenter.highlight', function (d3_event, d) {
77351 if (!d.relatedEntityIds || select(this).classed('disabled')) return;
77352 utilHighlightEntities(d.relatedEntityIds(), true, context);
77353 }).on('mouseleave.highlight', function (d3_event, d) {
77354 if (!d.relatedEntityIds) return;
77355 utilHighlightEntities(d.relatedEntityIds(), false, context);
77357 buttonsEnter.each(function (d) {
77358 var tooltip = uiTooltip().heading(d.title).title(d.tooltip()).keys([d.keys[0]]);
77360 _tooltips.push(tooltip);
77362 select(this).call(tooltip).append('div').attr('class', 'icon-wrap').call(svgIcon('#iD-operation-' + d.id, 'operation'));
77366 buttonsEnter.append('span').attr('class', 'label').html(function (d) {
77372 buttonsEnter.merge(buttons).classed('disabled', function (d) {
77373 return d.disabled();
77376 var initialScale = context.projection.scale();
77377 context.map().on('move.edit-menu', function () {
77378 if (initialScale !== context.projection.scale()) {
77381 }).on('drawn.edit-menu', function (info) {
77382 if (info.full) updatePosition();
77384 var lastPointerUpType; // `pointerup` is always called before `click`
77386 function pointerup(d3_event) {
77387 lastPointerUpType = d3_event.pointerType;
77390 function click(d3_event, operation) {
77391 d3_event.stopPropagation();
77393 if (operation.relatedEntityIds) {
77394 utilHighlightEntities(operation.relatedEntityIds(), false, context);
77397 if (operation.disabled()) {
77398 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
77399 // there are no tooltips for touch interactions so flash feedback instead
77400 context.ui().flash.duration(4000).iconName('#iD-operation-' + operation.id).iconClass('operation disabled').label(operation.tooltip)();
77403 if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
77404 context.ui().flash.duration(2000).iconName('#iD-operation-' + operation.id).iconClass('operation').label(operation.annotation() || operation.title)();
77411 lastPointerUpType = null;
77414 dispatch$1.call('toggled', this, true);
77417 function updatePosition() {
77418 if (!_menu || _menu.empty()) return;
77419 var anchorLoc = context.projection(_anchorLocLonLat);
77420 var viewport = context.surfaceRect();
77422 if (anchorLoc[0] < 0 || anchorLoc[0] > viewport.width || anchorLoc[1] < 0 || anchorLoc[1] > viewport.height) {
77423 // close the menu if it's gone offscreen
77428 var menuLeft = displayOnLeft(viewport);
77429 var offset = [0, 0];
77430 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
77433 if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
77434 // menu is near top viewport edge, shift downward
77435 offset[1] = -anchorLoc[1] + _vpTopMargin;
77437 offset[1] = -_menuHeight;
77440 if (anchorLoc[1] + _menuHeight > viewport.height - _vpBottomMargin) {
77441 // menu is near bottom viewport edge, shift upwards
77442 offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
77448 var origin = geoVecAdd(anchorLoc, offset);
77450 _menu.style('left', origin[0] + 'px').style('top', origin[1] + 'px');
77452 var tooltipSide = tooltipPosition(viewport, menuLeft);
77454 _tooltips.forEach(function (tooltip) {
77455 tooltip.placement(tooltipSide);
77458 function displayOnLeft(viewport) {
77459 if (_mainLocalizer.textDirection() === 'ltr') {
77460 if (anchorLoc[0] + _menuSideMargin + _menuWidth > viewport.width - _vpSideMargin) {
77461 // right menu would be too close to the right viewport edge, go left
77463 } // prefer right menu
77469 if (anchorLoc[0] - _menuSideMargin - _menuWidth < _vpSideMargin) {
77470 // left menu would be too close to the left viewport edge, go right
77472 } // prefer left menu
77479 function tooltipPosition(viewport, menuLeft) {
77480 if (_mainLocalizer.textDirection() === 'ltr') {
77482 // if there's not room for a right-side menu then there definitely
77483 // isn't room for right-side tooltips
77487 if (anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth > viewport.width - _vpSideMargin) {
77488 // right tooltips would be too close to the right viewport edge, go left
77490 } // prefer right tooltips
77500 if (anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth < _vpSideMargin) {
77501 // left tooltips would be too close to the left viewport edge, go right
77503 } // prefer left tooltips
77511 editMenu.close = function () {
77512 context.map().on('move.edit-menu', null).on('drawn.edit-menu', null);
77517 dispatch$1.call('toggled', this, false);
77520 editMenu.anchorLoc = function (val) {
77521 if (!arguments.length) return _anchorLoc;
77523 _anchorLocLonLat = context.projection.invert(_anchorLoc);
77527 editMenu.triggerType = function (val) {
77528 if (!arguments.length) return _triggerType;
77529 _triggerType = val;
77533 editMenu.operations = function (val) {
77534 if (!arguments.length) return _operations;
77539 return utilRebind(editMenu, dispatch$1, 'on');
77542 function uiFeatureInfo(context) {
77543 function update(selection) {
77544 var features = context.features();
77545 var stats = features.stats();
77547 var hiddenList = features.hidden().map(function (k) {
77550 return _t('inspector.title_count', {
77551 title: _t.html('feature.' + k + '.description'),
77557 }).filter(Boolean);
77558 selection.html('');
77560 if (hiddenList.length) {
77561 var tooltipBehavior = uiTooltip().placement('top').title(function () {
77562 return hiddenList.join('<br/>');
77564 selection.append('a').attr('class', 'chip').attr('href', '#').html(_t.html('feature_info.hidden_warning', {
77566 })).call(tooltipBehavior).on('click', function (d3_event) {
77567 tooltipBehavior.hide();
77568 d3_event.preventDefault(); // open the Map Data pane
77570 context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
77574 selection.classed('hide', !hiddenList.length);
77577 return function (selection) {
77579 context.features().on('change.feature_info', function () {
77585 function uiFlash(context) {
77588 var _duration = 2000;
77589 var _iconName = '#iD-icon-no';
77590 var _iconClass = 'disabled';
77595 _flashTimer.stop();
77598 context.container().select('.main-footer-wrap').classed('footer-hide', true).classed('footer-show', false);
77599 context.container().select('.flash-wrap').classed('footer-hide', false).classed('footer-show', true);
77600 var content = context.container().select('.flash-wrap').selectAll('.flash-content').data([0]); // Enter
77602 var contentEnter = content.enter().append('div').attr('class', 'flash-content');
77603 var iconEnter = contentEnter.append('svg').attr('class', 'flash-icon icon').append('g').attr('transform', 'translate(10,10)');
77604 iconEnter.append('circle').attr('r', 9);
77605 iconEnter.append('use').attr('transform', 'translate(-7,-7)').attr('width', '14').attr('height', '14');
77606 contentEnter.append('div').attr('class', 'flash-text'); // Update
77608 content = content.merge(contentEnter);
77609 content.selectAll('.flash-icon').attr('class', 'icon flash-icon ' + (_iconClass || ''));
77610 content.selectAll('.flash-icon use').attr('xlink:href', _iconName);
77611 content.selectAll('.flash-text').attr('class', 'flash-text').html(_label);
77612 _flashTimer = d3_timeout(function () {
77613 _flashTimer = null;
77614 context.container().select('.main-footer-wrap').classed('footer-hide', false).classed('footer-show', true);
77615 context.container().select('.flash-wrap').classed('footer-hide', true).classed('footer-show', false);
77620 flash.duration = function (_) {
77621 if (!arguments.length) return _duration;
77626 flash.label = function (_) {
77627 if (!arguments.length) return _label;
77632 flash.iconName = function (_) {
77633 if (!arguments.length) return _iconName;
77638 flash.iconClass = function (_) {
77639 if (!arguments.length) return _iconClass;
77647 function uiFullScreen(context) {
77648 var element = context.container().node(); // var button = d3_select(null);
77650 function getFullScreenFn() {
77651 if (element.requestFullscreen) {
77652 return element.requestFullscreen;
77653 } else if (element.msRequestFullscreen) {
77654 return element.msRequestFullscreen;
77655 } else if (element.mozRequestFullScreen) {
77656 return element.mozRequestFullScreen;
77657 } else if (element.webkitRequestFullscreen) {
77658 return element.webkitRequestFullscreen;
77662 function getExitFullScreenFn() {
77663 if (document.exitFullscreen) {
77664 return document.exitFullscreen;
77665 } else if (document.msExitFullscreen) {
77666 return document.msExitFullscreen;
77667 } else if (document.mozCancelFullScreen) {
77668 return document.mozCancelFullScreen;
77669 } else if (document.webkitExitFullscreen) {
77670 return document.webkitExitFullscreen;
77674 function isFullScreen() {
77675 return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;
77678 function isSupported() {
77679 return !!getFullScreenFn();
77682 function fullScreen(d3_event) {
77683 d3_event.preventDefault();
77685 if (!isFullScreen()) {
77686 // button.classed('active', true);
77687 getFullScreenFn().apply(element);
77689 // button.classed('active', false);
77690 getExitFullScreenFn().apply(document);
77694 return function () {
77696 if (!isSupported()) return; // button = selection.append('button')
77697 // .attr('title', t('full_screen'))
77698 // .on('click', fullScreen)
77700 // button.append('span')
77701 // .attr('class', 'icon full-screen');
77703 var detected = utilDetect();
77704 var keys = detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11'];
77705 context.keybinding().on(keys, fullScreen);
77709 function uiGeolocate(context) {
77710 var _geolocationOptions = {
77711 // prioritize speed and power usage over precision
77712 enableHighAccuracy: false,
77713 // don't hang indefinitely getting the location
77714 timeout: 6000 // 6sec
77718 var _locating = uiLoading(context).message(_t.html('geolocate.locating')).blocking(true);
77720 var _layer = context.layers().layer('geolocate');
77728 var _button = select(null);
77731 if (context.inIntro()) return;
77733 if (!_layer.enabled() && !_locating.isShown()) {
77734 // This timeout ensures that we still call finish() even if
77735 // the user declines to share their location in Firefox
77736 _timeoutID = setTimeout(error, 10000
77739 context.container().call(_locating); // get the latest position even if we already have one
77741 navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
77745 _layer.enabled(null, false);
77747 updateButtonState();
77751 function zoomTo() {
77752 context.enter(modeBrowse(context));
77753 var map = context.map();
77755 _layer.enabled(_position, true);
77757 updateButtonState();
77758 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
77761 function success(geolocation) {
77762 _position = geolocation;
77763 var coords = _position.coords;
77764 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
77771 // use the position from a previous call if we have one
77774 context.ui().flash.label(_t.html('geolocate.location_unavailable')).iconName('#iD-icon-geolocate')();
77780 function finish() {
77781 _locating.close(); // unblock ui
77785 clearTimeout(_timeoutID);
77788 _timeoutID = undefined;
77791 function updateButtonState() {
77792 _button.classed('active', _layer.enabled());
77795 return function (selection) {
77796 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) return;
77797 _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')]));
77798 context.keybinding().on(_t('geolocate.key'), click);
77802 function uiPanelBackground(context) {
77803 var background = context.background();
77804 var _currSourceName = null;
77805 var _metadata = {};
77806 var _metadataKeys = ['zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'];
77808 var debouncedRedraw = debounce(redraw, 250);
77810 function redraw(selection) {
77811 var source = background.baseLayerSource();
77812 if (!source) return;
77813 var isDG = source.id.match(/^DigitalGlobe/i) !== null;
77814 var sourceLabel = source.label();
77816 if (_currSourceName !== sourceLabel) {
77817 _currSourceName = sourceLabel;
77821 selection.html('');
77822 var list = selection.append('ul').attr('class', 'background-info');
77823 list.append('li').html(_currSourceName);
77825 _metadataKeys.forEach(function (k) {
77826 // DigitalGlobe vintage is available in raster layers for now.
77827 if (isDG && k === 'vintage') return;
77828 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]);
77831 debouncedGetMetadata(selection);
77832 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
77833 selection.append('a').html(_t.html('info_panels.background.' + toggleTiles)).attr('href', '#').attr('class', 'button button-toggle-tiles').on('click', function (d3_event) {
77834 d3_event.preventDefault();
77835 context.setDebug('tile', !context.getDebug('tile'));
77836 selection.call(redraw);
77840 var key = source.id + '-vintage';
77841 var sourceVintage = context.background().findSource(key);
77842 var showsVintage = context.background().showsLayer(sourceVintage);
77843 var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
77844 selection.append('a').html(_t.html('info_panels.background.' + toggleVintage)).attr('href', '#').attr('class', 'button button-toggle-vintage').on('click', function (d3_event) {
77845 d3_event.preventDefault();
77846 context.background().toggleOverlayLayer(sourceVintage);
77847 selection.call(redraw);
77849 } // disable if necessary
77852 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function (layerId) {
77853 if (source.id !== layerId) {
77854 var key = layerId + '-vintage';
77855 var sourceVintage = context.background().findSource(key);
77857 if (context.background().showsLayer(sourceVintage)) {
77858 context.background().toggleOverlayLayer(sourceVintage);
77864 var debouncedGetMetadata = debounce(getMetadata, 250);
77866 function getMetadata(selection) {
77867 var tile = context.container().select('.layer-background img.tile-center'); // tile near viewport center
77869 if (tile.empty()) return;
77870 var sourceName = _currSourceName;
77871 var d = tile.datum();
77872 var zoom = d && d.length >= 3 && d[2] || Math.floor(context.map().zoom());
77873 var center = context.map().center(); // update zoom
77875 _metadata.zoom = String(zoom);
77876 selection.selectAll('.background-info-list-zoom').classed('hide', false).selectAll('.background-info-span-zoom').html(_metadata.zoom);
77877 if (!d || !d.length >= 3) return;
77878 background.baseLayerSource().getMetadata(center, d, function (err, result) {
77879 if (err || _currSourceName !== sourceName) return; // update vintage
77881 var vintage = result.vintage;
77882 _metadata.vintage = vintage && vintage.range || _t('info_panels.background.unknown');
77883 selection.selectAll('.background-info-list-vintage').classed('hide', false).selectAll('.background-info-span-vintage').html(_metadata.vintage); // update other _metadata
77885 _metadataKeys.forEach(function (k) {
77886 if (k === 'zoom' || k === 'vintage') return; // done already
77888 var val = result[k];
77889 _metadata[k] = val;
77890 selection.selectAll('.background-info-list-' + k).classed('hide', !val).selectAll('.background-info-span-' + k).html(val);
77895 var panel = function panel(selection) {
77896 selection.call(redraw);
77897 context.map().on('drawn.info-background', function () {
77898 selection.call(debouncedRedraw);
77899 }).on('move.info-background', function () {
77900 selection.call(debouncedGetMetadata);
77904 panel.off = function () {
77905 context.map().on('drawn.info-background', null).on('move.info-background', null);
77908 panel.id = 'background';
77909 panel.label = _t.html('info_panels.background.title');
77910 panel.key = _t('info_panels.background.key');
77914 function uiPanelHistory(context) {
77917 function displayTimestamp(timestamp) {
77918 if (!timestamp) return _t('info_panels.history.unknown');
77927 var d = new Date(timestamp);
77928 if (isNaN(d.getTime())) return _t('info_panels.history.unknown');
77929 return d.toLocaleString(_mainLocalizer.localeCode(), options);
77932 function displayUser(selection, userName) {
77934 selection.append('span').html(_t.html('info_panels.history.unknown'));
77938 selection.append('span').attr('class', 'user-name').html(userName);
77939 var links = selection.append('div').attr('class', 'links');
77942 links.append('a').attr('class', 'user-osm-link').attr('href', osm.userURL(userName)).attr('target', '_blank').html('OSM');
77945 links.append('a').attr('class', 'user-hdyc-link').attr('href', 'https://hdyc.neis-one.org/?' + userName).attr('target', '_blank').attr('tabindex', -1).html('HDYC');
77948 function displayChangeset(selection, changeset) {
77950 selection.append('span').html(_t.html('info_panels.history.unknown'));
77954 selection.append('span').attr('class', 'changeset-id').html(changeset);
77955 var links = selection.append('div').attr('class', 'links');
77958 links.append('a').attr('class', 'changeset-osm-link').attr('href', osm.changesetURL(changeset)).attr('target', '_blank').html('OSM');
77961 links.append('a').attr('class', 'changeset-osmcha-link').attr('href', 'https://osmcha.org/changesets/' + changeset).attr('target', '_blank').html('OSMCha');
77962 links.append('a').attr('class', 'changeset-achavi-link').attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset).attr('target', '_blank').html('Achavi');
77965 function redraw(selection) {
77966 var selectedNoteID = context.selectedNoteID();
77967 osm = context.connection();
77968 var selected, note, entity;
77970 if (selectedNoteID && osm) {
77972 selected = [_t('note.note') + ' ' + selectedNoteID];
77973 note = osm.getNote(selectedNoteID);
77975 // selected 1..n entities
77976 selected = context.selectedIDs().filter(function (e) {
77977 return context.hasEntity(e);
77980 if (selected.length) {
77981 entity = context.entity(selected[0]);
77985 var singular = selected.length === 1 ? selected[0] : null;
77986 selection.html('');
77987 selection.append('h4').attr('class', 'history-heading').html(singular || _t.html('info_panels.selected', {
77990 if (!singular) return;
77993 selection.call(redrawEntity, entity);
77995 selection.call(redrawNote, note);
77999 function redrawNote(selection, note) {
78000 if (!note || note.isNew()) {
78001 selection.append('div').html(_t.html('info_panels.history.note_no_history'));
78005 var list = selection.append('ul');
78006 list.append('li').html(_t.html('info_panels.history.note_comments') + ':').append('span').html(note.comments.length);
78008 if (note.comments.length) {
78009 list.append('li').html(_t.html('info_panels.history.note_created_date') + ':').append('span').html(displayTimestamp(note.comments[0].date));
78010 list.append('li').html(_t.html('info_panels.history.note_created_user') + ':').call(displayUser, note.comments[0].user);
78014 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'));
78018 function redrawEntity(selection, entity) {
78019 if (!entity || entity.isNew()) {
78020 selection.append('div').html(_t.html('info_panels.history.no_history'));
78024 var links = selection.append('div').attr('class', 'links');
78027 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');
78030 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');
78031 var list = selection.append('ul');
78032 list.append('li').html(_t.html('info_panels.history.version') + ':').append('span').html(entity.version);
78033 list.append('li').html(_t.html('info_panels.history.last_edit') + ':').append('span').html(displayTimestamp(entity.timestamp));
78034 list.append('li').html(_t.html('info_panels.history.edited_by') + ':').call(displayUser, entity.user);
78035 list.append('li').html(_t.html('info_panels.history.changeset') + ':').call(displayChangeset, entity.changeset);
78038 var panel = function panel(selection) {
78039 selection.call(redraw);
78040 context.map().on('drawn.info-history', function () {
78041 selection.call(redraw);
78043 context.on('enter.info-history', function () {
78044 selection.call(redraw);
78048 panel.off = function () {
78049 context.map().on('drawn.info-history', null);
78050 context.on('enter.info-history', null);
78053 panel.id = 'history';
78054 panel.label = _t.html('info_panels.history.title');
78055 panel.key = _t('info_panels.history.key');
78059 var OSM_PRECISION = 7;
78061 * Returns a localized representation of the given length measurement.
78063 * @param {Number} m area in meters
78064 * @param {Boolean} isImperial true for U.S. customary units; false for metric
78067 function displayLength(m, isImperial) {
78068 var d = m * (isImperial ? 3.28084 : 1);
78081 unit = 'kilometers';
78087 return _t('units.' + unit, {
78088 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
78089 maximumSignificantDigits: 4
78094 * Returns a localized representation of the given area measurement.
78096 * @param {Number} m2 area in square meters
78097 * @param {Boolean} isImperial true for U.S. customary units; false for metric
78100 function displayArea(m2, isImperial) {
78101 var locale = _mainLocalizer.localeCode();
78102 var d = m2 * (isImperial ? 10.7639111056 : 1);
78108 if (d >= 6969600) {
78109 // > 0.25mi² show mi²
78111 unit1 = 'square_miles';
78114 unit1 = 'square_feet';
78117 if (d > 4356 && d < 43560000) {
78118 // 0.1 - 1000 acres
78124 // > 0.25km² show km²
78126 unit1 = 'square_kilometers';
78129 unit1 = 'square_meters';
78132 if (d > 1000 && d < 10000000) {
78133 // 0.1 - 1000 hectares
78135 unit2 = 'hectares';
78139 area = _t('units.' + unit1, {
78140 quantity: d1.toLocaleString(locale, {
78141 maximumSignificantDigits: 4
78146 return _t('units.area_pair', {
78148 area2: _t('units.' + unit2, {
78149 quantity: d2.toLocaleString(locale, {
78150 maximumSignificantDigits: 2
78159 function wrap$2(x, min, max) {
78161 return ((x - min) % d + d) % d + min;
78164 function clamp$1(x, min, max) {
78165 return Math.max(min, Math.min(x, max));
78168 function displayCoordinate(deg, pos, neg) {
78169 var locale = _mainLocalizer.localeCode();
78170 var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
78171 var sec = (min - Math.floor(min)) * 60;
78172 var displayDegrees = _t('units.arcdegrees', {
78173 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
78175 var displayCoordinate;
78177 if (Math.floor(sec) > 0) {
78178 displayCoordinate = displayDegrees + _t('units.arcminutes', {
78179 quantity: Math.floor(min).toLocaleString(locale)
78180 }) + _t('units.arcseconds', {
78181 quantity: Math.round(sec).toLocaleString(locale)
78183 } else if (Math.floor(min) > 0) {
78184 displayCoordinate = displayDegrees + _t('units.arcminutes', {
78185 quantity: Math.round(min).toLocaleString(locale)
78188 displayCoordinate = _t('units.arcdegrees', {
78189 quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
78194 return displayCoordinate;
78196 return _t('units.coordinate', {
78197 coordinate: displayCoordinate,
78198 direction: _t('units.' + (deg > 0 ? pos : neg))
78203 * Returns given coordinate pair in degree-minute-second format.
78205 * @param {Array<Number>} coord longitude and latitude
78209 function dmsCoordinatePair(coord) {
78210 return _t('units.coordinate_pair', {
78211 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
78212 longitude: displayCoordinate(wrap$2(coord[0], -180, 180), 'east', 'west')
78216 * Returns the given coordinate pair in decimal format.
78217 * note: unlocalized to avoid comma ambiguity - see #4765
78219 * @param {Array<Number>} coord longitude and latitude
78222 function decimalCoordinatePair(coord) {
78223 return _t('units.coordinate_pair', {
78224 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
78225 longitude: wrap$2(coord[0], -180, 180).toFixed(OSM_PRECISION)
78229 function uiPanelLocation(context) {
78230 var currLocation = '';
78232 function redraw(selection) {
78233 selection.html('');
78234 var list = selection.append('ul'); // Mouse coordinates
78236 var coord = context.map().mouseCoordinates();
78238 if (coord.some(isNaN)) {
78239 coord = context.map().center();
78242 list.append('li').html(dmsCoordinatePair(coord)).append('li').html(decimalCoordinatePair(coord)); // Location Info
78244 selection.append('div').attr('class', 'location-info').html(currLocation || ' ');
78245 debouncedGetLocation(selection, coord);
78248 var debouncedGetLocation = debounce(getLocation, 250);
78250 function getLocation(selection, coord) {
78251 if (!services.geocoder) {
78252 currLocation = _t('info_panels.location.unknown_location');
78253 selection.selectAll('.location-info').html(currLocation);
78255 services.geocoder.reverse(coord, function (err, result) {
78256 currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
78257 selection.selectAll('.location-info').html(currLocation);
78262 var panel = function panel(selection) {
78263 selection.call(redraw);
78264 context.surface().on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function () {
78265 selection.call(redraw);
78269 panel.off = function () {
78270 context.surface().on('.info-location', null);
78273 panel.id = 'location';
78274 panel.label = _t.html('info_panels.location.title');
78275 panel.key = _t('info_panels.location.key');
78279 function uiPanelMeasurement(context) {
78280 function radiansToMeters(r) {
78281 // using WGS84 authalic radius (6371007.1809 m)
78282 return r * 6371007.1809;
78285 function steradiansToSqmeters(r) {
78286 // http://gis.stackexchange.com/a/124857/40446
78287 return r / (4 * Math.PI) * 510065621724000;
78290 function toLineString(feature) {
78291 if (feature.type === 'LineString') return feature;
78293 type: 'LineString',
78297 if (feature.type === 'Polygon') {
78298 result.coordinates = feature.coordinates[0];
78299 } else if (feature.type === 'MultiPolygon') {
78300 result.coordinates = feature.coordinates[0][0];
78306 var _isImperial = !_mainLocalizer.usesMetric();
78308 function redraw(selection) {
78309 var graph = context.graph();
78310 var selectedNoteID = context.selectedNoteID();
78311 var osm = services.osm;
78312 var localeCode = _mainLocalizer.localeCode();
78314 var center, location, centroid;
78315 var closed, geometry;
78316 var totalNodeCount,
78321 if (selectedNoteID && osm) {
78323 var note = osm.getNote(selectedNoteID);
78324 heading = _t('note.note') + ' ' + selectedNoteID;
78325 location = note.loc;
78328 // selected 1..n entities
78329 var selectedIDs = context.selectedIDs().filter(function (id) {
78330 return context.hasEntity(id);
78332 var selected = selectedIDs.map(function (id) {
78333 return context.entity(id);
78335 heading = selected.length === 1 ? selected[0].id : _t('info_panels.selected', {
78339 if (selected.length) {
78340 var extent = geoExtent();
78342 for (var i in selected) {
78343 var entity = selected[i];
78345 extent._extend(entity.extent(graph));
78347 geometry = entity.geometry(graph);
78349 if (geometry === 'line' || geometry === 'area') {
78350 closed = entity.type === 'relation' || entity.isClosed() && !entity.isDegenerate();
78351 var feature = entity.asGeoJSON(graph);
78352 length += radiansToMeters(d3_geoLength(toLineString(feature))); // d3_geoCentroid is wrong for counterclockwise-wound polygons, so wind them clockwise
78354 centroid = d3_geoCentroid(geojsonRewind(Object.assign({}, feature), true));
78357 area += steradiansToSqmeters(entity.area(graph));
78362 if (selected.length > 1) {
78368 if (selected.length === 2 && selected[0].type === 'node' && selected[1].type === 'node') {
78369 distance = geoSphericalDistance(selected[0].loc, selected[1].loc);
78372 if (selected.length === 1 && selected[0].type === 'node') {
78373 location = selected[0].loc;
78375 totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
78378 if (!location && !centroid) {
78379 center = extent.center();
78384 selection.html('');
78387 selection.append('h4').attr('class', 'measurement-heading').html(heading);
78390 var list = selection.append('ul');
78394 list.append('li').html(_t.html('info_panels.measurement.geometry') + ':').append('span').html(closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry));
78397 if (totalNodeCount) {
78398 list.append('li').html(_t.html('info_panels.measurement.node_count') + ':').append('span').html(totalNodeCount.toLocaleString(localeCode));
78402 list.append('li').html(_t.html('info_panels.measurement.area') + ':').append('span').html(displayArea(area, _isImperial));
78406 list.append('li').html(_t.html('info_panels.measurement.' + (closed ? 'perimeter' : 'length')) + ':').append('span').html(displayLength(length, _isImperial));
78409 if (typeof distance === 'number') {
78410 list.append('li').html(_t.html('info_panels.measurement.distance') + ':').append('span').html(displayLength(distance, _isImperial));
78414 coordItem = list.append('li').html(_t.html('info_panels.measurement.location') + ':');
78415 coordItem.append('span').html(dmsCoordinatePair(location));
78416 coordItem.append('span').html(decimalCoordinatePair(location));
78420 coordItem = list.append('li').html(_t.html('info_panels.measurement.centroid') + ':');
78421 coordItem.append('span').html(dmsCoordinatePair(centroid));
78422 coordItem.append('span').html(decimalCoordinatePair(centroid));
78426 coordItem = list.append('li').html(_t.html('info_panels.measurement.center') + ':');
78427 coordItem.append('span').html(dmsCoordinatePair(center));
78428 coordItem.append('span').html(decimalCoordinatePair(center));
78431 if (length || area || typeof distance === 'number') {
78432 var toggle = _isImperial ? 'imperial' : 'metric';
78433 selection.append('a').html(_t.html('info_panels.measurement.' + toggle)).attr('href', '#').attr('class', 'button button-toggle-units').on('click', function (d3_event) {
78434 d3_event.preventDefault();
78435 _isImperial = !_isImperial;
78436 selection.call(redraw);
78441 var panel = function panel(selection) {
78442 selection.call(redraw);
78443 context.map().on('drawn.info-measurement', function () {
78444 selection.call(redraw);
78446 context.on('enter.info-measurement', function () {
78447 selection.call(redraw);
78451 panel.off = function () {
78452 context.map().on('drawn.info-measurement', null);
78453 context.on('enter.info-measurement', null);
78456 panel.id = 'measurement';
78457 panel.label = _t.html('info_panels.measurement.title');
78458 panel.key = _t('info_panels.measurement.key');
78462 var uiInfoPanels = {
78463 background: uiPanelBackground,
78464 history: uiPanelHistory,
78465 location: uiPanelLocation,
78466 measurement: uiPanelMeasurement
78469 function uiInfo(context) {
78470 var ids = Object.keys(uiInfoPanels);
78471 var wasActive = ['measurement'];
78473 var active = {}; // create panels
78475 ids.forEach(function (k) {
78477 panels[k] = uiInfoPanels[k](context);
78482 function info(selection) {
78483 function redraw() {
78484 var activeids = ids.filter(function (k) {
78487 var containers = infoPanels.selectAll('.panel-container').data(activeids, function (k) {
78490 containers.exit().style('opacity', 1).transition().duration(200).style('opacity', 0).on('end', function (d) {
78491 select(this).call(panels[d].off).remove();
78493 var enter = containers.enter().append('div').attr('class', function (d) {
78494 return 'fillD2 panel-container panel-container-' + d;
78496 enter.style('opacity', 0).transition().duration(200).style('opacity', 1);
78497 var title = enter.append('div').attr('class', 'panel-title fillD2');
78498 title.append('h3').html(function (d) {
78499 return panels[d].label;
78501 title.append('button').attr('class', 'close').on('click', function (d3_event, d) {
78502 d3_event.stopImmediatePropagation();
78503 d3_event.preventDefault();
78505 }).call(svgIcon('#iD-icon-close'));
78506 enter.append('div').attr('class', function (d) {
78507 return 'panel-content panel-content-' + d;
78508 }); // redraw the panels
78510 infoPanels.selectAll('.panel-content').each(function (d) {
78511 select(this).call(panels[d]);
78515 info.toggle = function (which) {
78516 var activeids = ids.filter(function (k) {
78522 active[which] = !active[which];
78524 if (activeids.length === 1 && activeids[0] === which) {
78525 // none active anymore
78526 wasActive = [which];
78529 context.container().select('.' + which + '-panel-toggle-item').classed('active', active[which]).select('input').property('checked', active[which]);
78532 if (activeids.length) {
78533 wasActive = activeids;
78534 activeids.forEach(function (k) {
78538 wasActive.forEach(function (k) {
78547 var infoPanels = selection.selectAll('.info-panels').data([0]);
78548 infoPanels = infoPanels.enter().append('div').attr('class', 'info-panels').merge(infoPanels);
78550 context.keybinding().on(uiCmd('⌘' + _t('info_panels.key')), function (d3_event) {
78551 d3_event.stopImmediatePropagation();
78552 d3_event.preventDefault();
78555 ids.forEach(function (k) {
78556 var key = _t('info_panels.' + k + '.key', {
78560 context.keybinding().on(uiCmd('⌘⇧' + key), function (d3_event) {
78561 d3_event.stopImmediatePropagation();
78562 d3_event.preventDefault();
78571 function pointBox(loc, context) {
78572 var rect = context.surfaceRect();
78573 var point = context.curtainProjection(loc);
78575 left: point[0] + rect.left - 40,
78576 top: point[1] + rect.top - 60,
78581 function pad(locOrBox, padding, context) {
78584 if (locOrBox instanceof Array) {
78585 var rect = context.surfaceRect();
78586 var point = context.curtainProjection(locOrBox);
78588 left: point[0] + rect.left,
78589 top: point[1] + rect.top
78596 left: box.left - padding,
78597 top: box.top - padding,
78598 width: (box.width || 0) + 2 * padding,
78599 height: (box.width || 0) + 2 * padding
78602 function icon(name, svgklass, useklass) {
78603 return '<svg class="icon ' + (svgklass || '') + '">' + '<use xlink:href="' + name + '"' + (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
78605 var helpStringReplacements; // Returns the localized HTML element for `id` with a standardized set of icon, key, and
78606 // label replacements suitable for tutorials and documentation. Optionally supplemented
78607 // with custom `replacements`
78609 function helpHtml(id, replacements) {
78610 // only load these the first time
78611 if (!helpStringReplacements) helpStringReplacements = {
78612 // insert icons corresponding to various UI elements
78613 point_icon: icon('#iD-icon-point', 'inline'),
78614 line_icon: icon('#iD-icon-line', 'inline'),
78615 area_icon: icon('#iD-icon-area', 'inline'),
78616 note_icon: icon('#iD-icon-note', 'inline add-note'),
78617 plus: icon('#iD-icon-plus', 'inline'),
78618 minus: icon('#iD-icon-minus', 'inline'),
78619 layers_icon: icon('#iD-icon-layers', 'inline'),
78620 data_icon: icon('#iD-icon-data', 'inline'),
78621 inspect: icon('#iD-icon-inspect', 'inline'),
78622 help_icon: icon('#iD-icon-help', 'inline'),
78623 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'inline'),
78624 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'inline'),
78625 save_icon: icon('#iD-icon-save', 'inline'),
78627 circularize_icon: icon('#iD-operation-circularize', 'inline operation'),
78628 continue_icon: icon('#iD-operation-continue', 'inline operation'),
78629 copy_icon: icon('#iD-operation-copy', 'inline operation'),
78630 delete_icon: icon('#iD-operation-delete', 'inline operation'),
78631 disconnect_icon: icon('#iD-operation-disconnect', 'inline operation'),
78632 downgrade_icon: icon('#iD-operation-downgrade', 'inline operation'),
78633 extract_icon: icon('#iD-operation-extract', 'inline operation'),
78634 merge_icon: icon('#iD-operation-merge', 'inline operation'),
78635 move_icon: icon('#iD-operation-move', 'inline operation'),
78636 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'inline operation'),
78637 paste_icon: icon('#iD-operation-paste', 'inline operation'),
78638 reflect_long_icon: icon('#iD-operation-reflect-long', 'inline operation'),
78639 reflect_short_icon: icon('#iD-operation-reflect-short', 'inline operation'),
78640 reverse_icon: icon('#iD-operation-reverse', 'inline operation'),
78641 rotate_icon: icon('#iD-operation-rotate', 'inline operation'),
78642 split_icon: icon('#iD-operation-split', 'inline operation'),
78643 straighten_icon: icon('#iD-operation-straighten', 'inline operation'),
78644 // interaction icons
78645 leftclick: icon('#iD-walkthrough-mouse-left', 'inline operation'),
78646 rightclick: icon('#iD-walkthrough-mouse-right', 'inline operation'),
78647 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'inline operation'),
78648 tap_icon: icon('#iD-walkthrough-tap', 'inline operation'),
78649 doubletap_icon: icon('#iD-walkthrough-doubletap', 'inline operation'),
78650 longpress_icon: icon('#iD-walkthrough-longpress', 'inline operation'),
78651 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'inline operation'),
78652 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'inline operation'),
78653 // insert keys; may be localized and platform-dependent
78654 shift: uiCmd.display('⇧'),
78655 alt: uiCmd.display('⌥'),
78656 "return": uiCmd.display('↵'),
78657 esc: _t.html('shortcuts.key.esc'),
78658 space: _t.html('shortcuts.key.space'),
78659 add_note_key: _t.html('modes.add_note.key'),
78660 help_key: _t.html('help.key'),
78661 shortcuts_key: _t.html('shortcuts.toggle.key'),
78662 // reference localized UI labels directly so that they'll always match
78663 save: _t.html('save.title'),
78664 undo: _t.html('undo.title'),
78665 redo: _t.html('redo.title'),
78666 upload: _t.html('commit.save'),
78667 point: _t.html('modes.add_point.title'),
78668 line: _t.html('modes.add_line.title'),
78669 area: _t.html('modes.add_area.title'),
78670 note: _t.html('modes.add_note.label'),
78671 circularize: _t.html('operations.circularize.title'),
78672 "continue": _t.html('operations.continue.title'),
78673 copy: _t.html('operations.copy.title'),
78674 "delete": _t.html('operations.delete.title'),
78675 disconnect: _t.html('operations.disconnect.title'),
78676 downgrade: _t.html('operations.downgrade.title'),
78677 extract: _t.html('operations.extract.title'),
78678 merge: _t.html('operations.merge.title'),
78679 move: _t.html('operations.move.title'),
78680 orthogonalize: _t.html('operations.orthogonalize.title'),
78681 paste: _t.html('operations.paste.title'),
78682 reflect_long: _t.html('operations.reflect.title.long'),
78683 reflect_short: _t.html('operations.reflect.title.short'),
78684 reverse: _t.html('operations.reverse.title'),
78685 rotate: _t.html('operations.rotate.title'),
78686 split: _t.html('operations.split.title'),
78687 straighten: _t.html('operations.straighten.title'),
78688 map_data: _t.html('map_data.title'),
78689 osm_notes: _t.html('map_data.layers.notes.title'),
78690 fields: _t.html('inspector.fields'),
78691 tags: _t.html('inspector.tags'),
78692 relations: _t.html('inspector.relations'),
78693 new_relation: _t.html('inspector.new_relation'),
78694 turn_restrictions: _t.html('presets.fields.restrictions.label'),
78695 background_settings: _t.html('background.description'),
78696 imagery_offset: _t.html('background.fix_misalignment'),
78697 start_the_walkthrough: _t.html('splash.walkthrough'),
78698 help: _t.html('help.title'),
78699 ok: _t.html('intro.ok')
78703 if (replacements) {
78704 reps = Object.assign(replacements, helpStringReplacements);
78706 reps = helpStringReplacements;
78709 return _t.html(id, reps) // use keyboard key styling for shortcuts
78710 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
78713 function slugify(text) {
78714 return text.toString().toLowerCase().replace(/\s+/g, '-') // Replace spaces with -
78715 .replace(/[^\w\-]+/g, '') // Remove all non-word chars
78716 .replace(/\-\-+/g, '-') // Replace multiple - with single -
78717 .replace(/^-+/, '') // Trim - from start of text
78718 .replace(/-+$/, ''); // Trim - from end of text
78719 } // console warning for missing walkthrough names
78722 var missingStrings = {};
78724 function checkKey(key, text) {
78726 "default": undefined
78727 }) === undefined) {
78728 if (missingStrings.hasOwnProperty(key)) return; // warn once
78730 missingStrings[key] = text;
78731 var missing = key + ': ' + text;
78732 if (typeof console !== 'undefined') console.log(missing); // eslint-disable-line
78736 function localize(obj) {
78737 var key; // Assign name if entity has one..
78739 var name = obj.tags && obj.tags.name;
78742 key = 'intro.graph.name.' + slugify(name);
78743 obj.tags.name = _t(key, {
78746 checkKey(key, name);
78747 } // Assign street name if entity has one..
78750 var street = obj.tags && obj.tags['addr:street'];
78753 key = 'intro.graph.name.' + slugify(street);
78754 obj.tags['addr:street'] = _t(key, {
78757 checkKey(key, street); // Add address details common across walkthrough..
78759 var addrTags = ['block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood', 'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'];
78760 addrTags.forEach(function (k) {
78761 var key = 'intro.graph.' + k;
78762 var tag = 'addr:' + k;
78763 var val = obj.tags && obj.tags[tag];
78764 var str = _t(key, {
78769 if (str.match(/^<.*>$/) !== null) {
78770 delete obj.tags[tag];
78772 obj.tags[tag] = str;
78779 } // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
78781 function isMostlySquare(points) {
78782 // note: uses 15 here instead of the 12 from actionOrthogonalize because
78783 // actionOrthogonalize can actually straighten some larger angles as it iterates
78784 var threshold = 15; // degrees within right or straight
78786 var lowerBound = Math.cos((90 - threshold) * Math.PI / 180); // near right
78788 var upperBound = Math.cos(threshold * Math.PI / 180); // near straight
78790 for (var i = 0; i < points.length; i++) {
78791 var a = points[(i - 1 + points.length) % points.length];
78792 var origin = points[i];
78793 var b = points[(i + 1) % points.length];
78794 var dotp = geoVecNormalizedDot(a, b, origin);
78795 var mag = Math.abs(dotp);
78797 if (mag > lowerBound && mag < upperBound) {
78804 function selectMenuItem(context, operation) {
78805 return context.container().select('.edit-menu .edit-menu-item-' + operation);
78807 function transitionTime(point1, point2) {
78808 var distance = geoSphericalDistance(point1, point2);
78809 if (distance === 0) return 0;else if (distance < 80) return 500;else return 1000;
78812 function uiCurtain(containerNode) {
78813 var surface = select(null),
78814 tooltip = select(null),
78815 darkness = select(null);
78817 function curtain(selection) {
78818 surface = selection.append('svg').attr('class', 'curtain').style('top', 0).style('left', 0);
78819 darkness = surface.append('path').attr('x', 0).attr('y', 0).attr('class', 'curtain-darkness');
78820 select(window).on('resize.curtain', resize);
78821 tooltip = selection.append('div').attr('class', 'tooltip');
78822 tooltip.append('div').attr('class', 'popover-arrow');
78823 tooltip.append('div').attr('class', 'popover-inner');
78826 function resize() {
78827 surface.attr('width', containerNode.clientWidth).attr('height', containerNode.clientHeight);
78828 curtain.cut(darkness.datum());
78832 * Reveal cuts the curtain to highlight the given box,
78833 * and shows a tooltip with instructions next to the box.
78835 * @param {String|ClientRect} [box] box used to cut the curtain
78836 * @param {String} [text] text for a tooltip
78837 * @param {Object} [options]
78838 * @param {string} [options.tooltipClass] optional class to add to the tooltip
78839 * @param {integer} [options.duration] transition time in milliseconds
78840 * @param {string} [options.buttonText] if set, create a button with this text label
78841 * @param {function} [options.buttonCallback] if set, the callback for the button
78842 * @param {function} [options.padding] extra margin in px to put around bbox
78843 * @param {String|ClientRect} [options.tooltipBox] box for tooltip position, if different from box for the curtain
78847 curtain.reveal = function (box, html, options) {
78848 options = options || {};
78850 if (typeof box === 'string') {
78851 box = select(box).node();
78854 if (box && box.getBoundingClientRect) {
78855 box = copyBox(box.getBoundingClientRect());
78856 var containerRect = containerNode.getBoundingClientRect();
78857 box.top -= containerRect.top;
78858 box.left -= containerRect.left;
78861 if (box && options.padding) {
78862 box.top -= options.padding;
78863 box.left -= options.padding;
78864 box.bottom += options.padding;
78865 box.right += options.padding;
78866 box.height += options.padding * 2;
78867 box.width += options.padding * 2;
78872 if (options.tooltipBox) {
78873 tooltipBox = options.tooltipBox;
78875 if (typeof tooltipBox === 'string') {
78876 tooltipBox = select(tooltipBox).node();
78879 if (tooltipBox && tooltipBox.getBoundingClientRect) {
78880 tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
78886 if (tooltipBox && html) {
78887 if (html.indexOf('**') !== -1) {
78888 if (html.indexOf('<span') === 0) {
78889 html = html.replace(/^(<span.*?>)(.+?)(\*\*)/, '$1<span>$2</span>$3');
78891 html = html.replace(/^(.+?)(\*\*)/, '<span>$1</span>$2');
78892 } // pseudo markdown bold text for the instruction section..
78895 html = html.replace(/\*\*(.*?)\*\*/g, '<span class="instruction">$1</span>');
78898 html = html.replace(/\*(.*?)\*/g, '<em>$1</em>'); // emphasis
78900 html = html.replace(/\{br\}/g, '<br/><br/>'); // linebreak
78902 if (options.buttonText && options.buttonCallback) {
78903 html += '<div class="button-section">' + '<button href="#" class="button action">' + options.buttonText + '</button></div>';
78906 var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
78907 tooltip.classed(classes, true).selectAll('.popover-inner').html(html);
78909 if (options.buttonText && options.buttonCallback) {
78910 var button = tooltip.selectAll('.button-section .button.action');
78911 button.on('click', function (d3_event) {
78912 d3_event.preventDefault();
78913 options.buttonCallback();
78917 var tip = copyBox(tooltip.node().getBoundingClientRect()),
78918 w = containerNode.clientWidth,
78919 h = containerNode.clientHeight,
78920 tooltipWidth = 200,
78923 pos; // hack: this will have bottom placement,
78924 // so need to reserve extra space for the tooltip illustration.
78926 if (options.tooltipClass === 'intro-mouse') {
78928 } // trim box dimensions to just the portion that fits in the container..
78931 if (tooltipBox.top + tooltipBox.height > h) {
78932 tooltipBox.height -= tooltipBox.top + tooltipBox.height - h;
78935 if (tooltipBox.left + tooltipBox.width > w) {
78936 tooltipBox.width -= tooltipBox.left + tooltipBox.width - w;
78937 } // determine tooltip placement..
78940 if (tooltipBox.top + tooltipBox.height < 100) {
78941 // tooltip below box..
78943 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top + tooltipBox.height];
78944 } else if (tooltipBox.top > h - 140) {
78945 // tooltip above box..
78947 pos = [tooltipBox.left + tooltipBox.width / 2 - tip.width / 2, tooltipBox.top - tip.height];
78949 // tooltip to the side of the tooltipBox..
78950 var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
78952 if (_mainLocalizer.textDirection() === 'rtl') {
78953 if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
78955 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
78958 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
78961 if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
78963 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
78966 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
78971 if (options.duration !== 0 || !tooltip.classed(side)) {
78972 tooltip.call(uiToggle(true));
78975 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
78976 // (doesn't affect the placement of the popover-arrow)
78980 if (side === 'left' || side === 'right') {
78982 shiftY = 60 - pos[1];
78983 } else if (pos[1] + tip.height > h - 100) {
78984 shiftY = h - pos[1] - tip.height - 100;
78988 tooltip.selectAll('.popover-inner').style('top', shiftY + 'px');
78990 tooltip.classed('in', false).call(uiToggle(false));
78993 curtain.cut(box, options.duration);
78997 curtain.cut = function (datum, duration) {
78998 darkness.datum(datum).interrupt();
79001 if (duration === 0) {
79002 selection = darkness;
79004 selection = darkness.transition().duration(duration || 600).ease(linear$1);
79007 selection.attr('d', function (d) {
79008 var containerWidth = containerNode.clientWidth;
79009 var containerHeight = containerNode.clientHeight;
79010 var string = 'M 0,0 L 0,' + containerHeight + ' L ' + containerWidth + ',' + containerHeight + 'L' + containerWidth + ',0 Z';
79011 if (!d) return string;
79012 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';
79016 curtain.remove = function () {
79019 select(window).on('resize.curtain', null);
79020 }; // ClientRects are immutable, so copy them to an object,
79021 // in case we need to trim the height/width.
79024 function copyBox(src) {
79028 bottom: src.bottom,
79038 function uiIntroWelcome(context, reveal) {
79039 var dispatch$1 = dispatch('done');
79041 title: 'intro.welcome.title'
79044 function welcome() {
79045 context.map().centerZoom([-85.63591, 41.94285], 19);
79046 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.welcome'), {
79047 buttonText: _t.html('intro.ok'),
79048 buttonCallback: practice
79052 function practice() {
79053 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.practice'), {
79054 buttonText: _t.html('intro.ok'),
79055 buttonCallback: words
79060 reveal('.intro-nav-wrap .chapter-welcome', helpHtml('intro.welcome.words'), {
79061 buttonText: _t.html('intro.ok'),
79062 buttonCallback: chapters
79066 function chapters() {
79067 dispatch$1.call('done');
79068 reveal('.intro-nav-wrap .chapter-navigation', helpHtml('intro.welcome.chapters', {
79069 next: _t('intro.navigation.title')
79073 chapter.enter = function () {
79077 chapter.exit = function () {
79078 context.container().select('.curtain-tooltip.intro-mouse').selectAll('.counter').remove();
79081 chapter.restart = function () {
79086 return utilRebind(chapter, dispatch$1, 'on');
79089 function uiIntroNavigation(context, reveal) {
79090 var dispatch$1 = dispatch('done');
79092 var hallId = 'n2061';
79093 var townHall = [-85.63591, 41.94285];
79094 var springStreetId = 'w397';
79095 var springStreetEndId = 'n1834';
79096 var springStreet = [-85.63582, 41.94255];
79097 var onewayField = _mainPresetIndex.field('oneway');
79098 var maxspeedField = _mainPresetIndex.field('maxspeed');
79100 title: 'intro.navigation.title'
79103 function timeout(f, t) {
79104 timeouts.push(window.setTimeout(f, t));
79107 function eventCancel(d3_event) {
79108 d3_event.stopPropagation();
79109 d3_event.preventDefault();
79112 function isTownHallSelected() {
79113 var ids = context.selectedIDs();
79114 return ids.length === 1 && ids[0] === hallId;
79117 function dragMap() {
79118 context.enter(modeBrowse(context));
79119 context.history().reset('initial');
79120 var msec = transitionTime(townHall, context.map().center());
79123 reveal(null, null, {
79128 context.map().centerZoomEase(townHall, 19, msec);
79129 timeout(function () {
79130 var centerStart = context.map().center();
79131 var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
79132 var dragString = helpHtml('intro.navigation.map_info') + '{br}' + helpHtml('intro.navigation.' + textId);
79133 reveal('.surface', dragString);
79134 context.map().on('drawn.intro', function () {
79135 reveal('.surface', dragString, {
79139 context.map().on('move.intro', function () {
79140 var centerNow = context.map().center();
79142 if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
79143 context.map().on('move.intro', null);
79144 timeout(function () {
79145 continueTo(zoomMap);
79151 function continueTo(nextStep) {
79152 context.map().on('move.intro drawn.intro', null);
79157 function zoomMap() {
79158 var zoomStart = context.map().zoom();
79159 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
79160 var zoomString = helpHtml('intro.navigation.' + textId);
79161 reveal('.surface', zoomString);
79162 context.map().on('drawn.intro', function () {
79163 reveal('.surface', zoomString, {
79167 context.map().on('move.intro', function () {
79168 if (context.map().zoom() !== zoomStart) {
79169 context.map().on('move.intro', null);
79170 timeout(function () {
79171 continueTo(features);
79176 function continueTo(nextStep) {
79177 context.map().on('move.intro drawn.intro', null);
79182 function features() {
79183 var onClick = function onClick() {
79184 continueTo(pointsLinesAreas);
79187 reveal('.surface', helpHtml('intro.navigation.features'), {
79188 buttonText: _t.html('intro.ok'),
79189 buttonCallback: onClick
79191 context.map().on('drawn.intro', function () {
79192 reveal('.surface', helpHtml('intro.navigation.features'), {
79194 buttonText: _t.html('intro.ok'),
79195 buttonCallback: onClick
79199 function continueTo(nextStep) {
79200 context.map().on('drawn.intro', null);
79205 function pointsLinesAreas() {
79206 var onClick = function onClick() {
79207 continueTo(nodesWays);
79210 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
79211 buttonText: _t.html('intro.ok'),
79212 buttonCallback: onClick
79214 context.map().on('drawn.intro', function () {
79215 reveal('.surface', helpHtml('intro.navigation.points_lines_areas'), {
79217 buttonText: _t.html('intro.ok'),
79218 buttonCallback: onClick
79222 function continueTo(nextStep) {
79223 context.map().on('drawn.intro', null);
79228 function nodesWays() {
79229 var onClick = function onClick() {
79230 continueTo(clickTownHall);
79233 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
79234 buttonText: _t.html('intro.ok'),
79235 buttonCallback: onClick
79237 context.map().on('drawn.intro', function () {
79238 reveal('.surface', helpHtml('intro.navigation.nodes_ways'), {
79240 buttonText: _t.html('intro.ok'),
79241 buttonCallback: onClick
79245 function continueTo(nextStep) {
79246 context.map().on('drawn.intro', null);
79251 function clickTownHall() {
79252 context.enter(modeBrowse(context));
79253 context.history().reset('initial');
79254 var entity = context.hasEntity(hallId);
79255 if (!entity) return;
79256 reveal(null, null, {
79259 context.map().centerZoomEase(entity.loc, 19, 500);
79260 timeout(function () {
79261 var entity = context.hasEntity(hallId);
79262 if (!entity) return;
79263 var box = pointBox(entity.loc, context);
79264 var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
79265 reveal(box, helpHtml('intro.navigation.' + textId));
79266 context.map().on('move.intro drawn.intro', function () {
79267 var entity = context.hasEntity(hallId);
79268 if (!entity) return;
79269 var box = pointBox(entity.loc, context);
79270 reveal(box, helpHtml('intro.navigation.' + textId), {
79274 context.on('enter.intro', function () {
79275 if (isTownHallSelected()) continueTo(selectedTownHall);
79277 }, 550); // after centerZoomEase
79279 context.history().on('change.intro', function () {
79280 if (!context.hasEntity(hallId)) {
79281 continueTo(clickTownHall);
79285 function continueTo(nextStep) {
79286 context.on('enter.intro', null);
79287 context.map().on('move.intro drawn.intro', null);
79288 context.history().on('change.intro', null);
79293 function selectedTownHall() {
79294 if (!isTownHallSelected()) return clickTownHall();
79295 var entity = context.hasEntity(hallId);
79296 if (!entity) return clickTownHall();
79297 var box = pointBox(entity.loc, context);
79299 var onClick = function onClick() {
79300 continueTo(editorTownHall);
79303 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
79304 buttonText: _t.html('intro.ok'),
79305 buttonCallback: onClick
79307 context.map().on('move.intro drawn.intro', function () {
79308 var entity = context.hasEntity(hallId);
79309 if (!entity) return;
79310 var box = pointBox(entity.loc, context);
79311 reveal(box, helpHtml('intro.navigation.selected_townhall'), {
79313 buttonText: _t.html('intro.ok'),
79314 buttonCallback: onClick
79317 context.history().on('change.intro', function () {
79318 if (!context.hasEntity(hallId)) {
79319 continueTo(clickTownHall);
79323 function continueTo(nextStep) {
79324 context.map().on('move.intro drawn.intro', null);
79325 context.history().on('change.intro', null);
79330 function editorTownHall() {
79331 if (!isTownHallSelected()) return clickTownHall(); // disallow scrolling
79333 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79335 var onClick = function onClick() {
79336 continueTo(presetTownHall);
79339 reveal('.entity-editor-pane', helpHtml('intro.navigation.editor_townhall'), {
79340 buttonText: _t.html('intro.ok'),
79341 buttonCallback: onClick
79343 context.on('exit.intro', function () {
79344 continueTo(clickTownHall);
79346 context.history().on('change.intro', function () {
79347 if (!context.hasEntity(hallId)) {
79348 continueTo(clickTownHall);
79352 function continueTo(nextStep) {
79353 context.on('exit.intro', null);
79354 context.history().on('change.intro', null);
79355 context.container().select('.inspector-wrap').on('wheel.intro', null);
79360 function presetTownHall() {
79361 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
79363 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
79365 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel); // preset match, in case the user happened to change it.
79367 var entity = context.entity(context.selectedIDs()[0]);
79368 var preset = _mainPresetIndex.match(entity, context.graph());
79370 var onClick = function onClick() {
79371 continueTo(fieldsTownHall);
79374 reveal('.entity-editor-pane .section-feature-type', helpHtml('intro.navigation.preset_townhall', {
79375 preset: preset.name()
79377 buttonText: _t.html('intro.ok'),
79378 buttonCallback: onClick
79380 context.on('exit.intro', function () {
79381 continueTo(clickTownHall);
79383 context.history().on('change.intro', function () {
79384 if (!context.hasEntity(hallId)) {
79385 continueTo(clickTownHall);
79389 function continueTo(nextStep) {
79390 context.on('exit.intro', null);
79391 context.history().on('change.intro', null);
79392 context.container().select('.inspector-wrap').on('wheel.intro', null);
79397 function fieldsTownHall() {
79398 if (!isTownHallSelected()) return clickTownHall(); // reset pane, in case user happened to change it..
79400 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // disallow scrolling
79402 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79404 var onClick = function onClick() {
79405 continueTo(closeTownHall);
79408 reveal('.entity-editor-pane .section-preset-fields', helpHtml('intro.navigation.fields_townhall'), {
79409 buttonText: _t.html('intro.ok'),
79410 buttonCallback: onClick
79412 context.on('exit.intro', function () {
79413 continueTo(clickTownHall);
79415 context.history().on('change.intro', function () {
79416 if (!context.hasEntity(hallId)) {
79417 continueTo(clickTownHall);
79421 function continueTo(nextStep) {
79422 context.on('exit.intro', null);
79423 context.history().on('change.intro', null);
79424 context.container().select('.inspector-wrap').on('wheel.intro', null);
79429 function closeTownHall() {
79430 if (!isTownHallSelected()) return clickTownHall();
79431 var selector = '.entity-editor-pane button.close svg use';
79432 var href = select(selector).attr('href') || '#iD-icon-close';
79433 reveal('.entity-editor-pane', helpHtml('intro.navigation.close_townhall', {
79434 button: icon(href, 'inline')
79436 context.on('exit.intro', function () {
79437 continueTo(searchStreet);
79439 context.history().on('change.intro', function () {
79440 // update the close icon in the tooltip if the user edits something.
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.close_townhall', {
79444 button: icon(href, 'inline')
79450 function continueTo(nextStep) {
79451 context.on('exit.intro', null);
79452 context.history().on('change.intro', null);
79457 function searchStreet() {
79458 context.enter(modeBrowse(context));
79459 context.history().reset('initial'); // ensure spring street exists
79461 var msec = transitionTime(springStreet, context.map().center());
79464 reveal(null, null, {
79469 context.map().centerZoomEase(springStreet, 19, msec); // ..and user can see it
79471 timeout(function () {
79472 reveal('.search-header input', helpHtml('intro.navigation.search_street', {
79473 name: _t('intro.graph.name.spring-street')
79475 context.container().select('.search-header input').on('keyup.intro', checkSearchResult);
79479 function checkSearchResult() {
79480 var first = context.container().select('.feature-list-item:nth-child(0n+2)'); // skip "No Results" item
79482 var firstName = first.select('.entity-name');
79483 var name = _t('intro.graph.name.spring-street');
79485 if (!firstName.empty() && firstName.html() === name) {
79486 reveal(first.node(), helpHtml('intro.navigation.choose_street', {
79491 context.on('exit.intro', function () {
79492 continueTo(selectedStreet);
79494 context.container().select('.search-header input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
79497 function continueTo(nextStep) {
79498 context.on('exit.intro', null);
79499 context.container().select('.search-header input').on('keydown.intro', null).on('keyup.intro', null);
79504 function selectedStreet() {
79505 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
79506 return searchStreet();
79509 var onClick = function onClick() {
79510 continueTo(editorStreet);
79513 var entity = context.entity(springStreetEndId);
79514 var box = pointBox(entity.loc, context);
79516 reveal(box, helpHtml('intro.navigation.selected_street', {
79517 name: _t('intro.graph.name.spring-street')
79520 buttonText: _t.html('intro.ok'),
79521 buttonCallback: onClick
79523 timeout(function () {
79524 context.map().on('move.intro drawn.intro', function () {
79525 var entity = context.hasEntity(springStreetEndId);
79526 if (!entity) return;
79527 var box = pointBox(entity.loc, context);
79529 reveal(box, helpHtml('intro.navigation.selected_street', {
79530 name: _t('intro.graph.name.spring-street')
79533 buttonText: _t.html('intro.ok'),
79534 buttonCallback: onClick
79537 }, 600); // after reveal.
79539 context.on('enter.intro', function (mode) {
79540 if (!context.hasEntity(springStreetId)) {
79541 return continueTo(searchStreet);
79544 var ids = context.selectedIDs();
79546 if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
79547 // keep Spring Street selected..
79548 context.enter(modeSelect(context, [springStreetId]));
79551 context.history().on('change.intro', function () {
79552 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
79553 timeout(function () {
79554 continueTo(searchStreet);
79555 }, 300); // after any transition (e.g. if user deleted intersection)
79559 function continueTo(nextStep) {
79560 context.map().on('move.intro drawn.intro', null);
79561 context.on('enter.intro', null);
79562 context.history().on('change.intro', null);
79567 function editorStreet() {
79568 var selector = '.entity-editor-pane button.close svg use';
79569 var href = select(selector).attr('href') || '#iD-icon-close';
79570 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
79571 button: icon(href, 'inline'),
79572 field1: onewayField.label(),
79573 field2: maxspeedField.label()
79575 context.on('exit.intro', function () {
79578 context.history().on('change.intro', function () {
79579 // update the close icon in the tooltip if the user edits something.
79580 var selector = '.entity-editor-pane button.close svg use';
79581 var href = select(selector).attr('href') || '#iD-icon-close';
79582 reveal('.entity-editor-pane', helpHtml('intro.navigation.street_different_fields') + '{br}' + helpHtml('intro.navigation.editor_street', {
79583 button: icon(href, 'inline'),
79584 field1: onewayField.label(),
79585 field2: maxspeedField.label()
79591 function continueTo(nextStep) {
79592 context.on('exit.intro', null);
79593 context.history().on('change.intro', null);
79599 dispatch$1.call('done');
79600 reveal('.ideditor', helpHtml('intro.navigation.play', {
79601 next: _t('intro.points.title')
79603 tooltipBox: '.intro-nav-wrap .chapter-point',
79604 buttonText: _t.html('intro.ok'),
79605 buttonCallback: function buttonCallback() {
79606 reveal('.ideditor');
79611 chapter.enter = function () {
79615 chapter.exit = function () {
79616 timeouts.forEach(window.clearTimeout);
79617 context.on('enter.intro exit.intro', null);
79618 context.map().on('move.intro drawn.intro', null);
79619 context.history().on('change.intro', null);
79620 context.container().select('.inspector-wrap').on('wheel.intro', null);
79621 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
79624 chapter.restart = function () {
79629 return utilRebind(chapter, dispatch$1, 'on');
79632 function uiIntroPoint(context, reveal) {
79633 var dispatch$1 = dispatch('done');
79635 var intersection = [-85.63279, 41.94394];
79636 var building = [-85.632422, 41.944045];
79637 var cafePreset = _mainPresetIndex.item('amenity/cafe');
79638 var _pointID = null;
79640 title: 'intro.points.title'
79643 function timeout(f, t) {
79644 timeouts.push(window.setTimeout(f, t));
79647 function eventCancel(d3_event) {
79648 d3_event.stopPropagation();
79649 d3_event.preventDefault();
79652 function addPoint() {
79653 context.enter(modeBrowse(context));
79654 context.history().reset('initial');
79655 var msec = transitionTime(intersection, context.map().center());
79658 reveal(null, null, {
79663 context.map().centerZoomEase(intersection, 19, msec);
79664 timeout(function () {
79665 var tooltip = reveal('button.add-point', helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));
79667 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-points');
79668 context.on('enter.intro', function (mode) {
79669 if (mode.id !== 'add-point') return;
79670 continueTo(placePoint);
79674 function continueTo(nextStep) {
79675 context.on('enter.intro', null);
79680 function placePoint() {
79681 if (context.mode().id !== 'add-point') {
79682 return chapter.restart();
79685 var pointBox = pad(building, 150, context);
79686 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
79687 reveal(pointBox, helpHtml('intro.points.' + textId));
79688 context.map().on('move.intro drawn.intro', function () {
79689 pointBox = pad(building, 150, context);
79690 reveal(pointBox, helpHtml('intro.points.' + textId), {
79694 context.on('enter.intro', function (mode) {
79695 if (mode.id !== 'select') return chapter.restart();
79696 _pointID = context.mode().selectedIDs()[0];
79697 continueTo(searchPreset);
79700 function continueTo(nextStep) {
79701 context.map().on('move.intro drawn.intro', null);
79702 context.on('enter.intro', null);
79707 function searchPreset() {
79708 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79710 } // disallow scrolling
79713 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79714 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
79715 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
79716 preset: cafePreset.name()
79718 context.on('enter.intro', function (mode) {
79719 if (!_pointID || !context.hasEntity(_pointID)) {
79720 return continueTo(addPoint);
79723 var ids = context.selectedIDs();
79725 if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
79726 // keep the user's point selected..
79727 context.enter(modeSelect(context, [_pointID])); // disallow scrolling
79729 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79730 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
79731 reveal('.preset-search-input', helpHtml('intro.points.search_cafe', {
79732 preset: cafePreset.name()
79734 context.history().on('change.intro', null);
79738 function checkPresetSearch() {
79739 var first = context.container().select('.preset-list-item:first-child');
79741 if (first.classed('preset-amenity-cafe')) {
79742 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
79743 reveal(first.select('.preset-list-button').node(), helpHtml('intro.points.choose_cafe', {
79744 preset: cafePreset.name()
79748 context.history().on('change.intro', function () {
79749 continueTo(aboutFeatureEditor);
79754 function continueTo(nextStep) {
79755 context.on('enter.intro', null);
79756 context.history().on('change.intro', null);
79757 context.container().select('.inspector-wrap').on('wheel.intro', null);
79758 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
79763 function aboutFeatureEditor() {
79764 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79768 timeout(function () {
79769 reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
79770 tooltipClass: 'intro-points-describe',
79771 buttonText: _t.html('intro.ok'),
79772 buttonCallback: function buttonCallback() {
79773 continueTo(addName);
79777 context.on('exit.intro', function () {
79778 // if user leaves select mode here, just continue with the tutorial.
79779 continueTo(reselectPoint);
79782 function continueTo(nextStep) {
79783 context.on('exit.intro', null);
79788 function addName() {
79789 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79791 } // reset pane, in case user happened to change it..
79794 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79795 var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name');
79796 timeout(function () {
79797 // It's possible for the user to add a name in a previous step..
79798 // If so, don't tell them to add the name in this step.
79799 // Give them an OK button instead.
79800 var entity = context.entity(_pointID);
79802 if (entity.tags.name) {
79803 var tooltip = reveal('.entity-editor-pane', addNameString, {
79804 tooltipClass: 'intro-points-describe',
79805 buttonText: _t.html('intro.ok'),
79806 buttonCallback: function buttonCallback() {
79807 continueTo(addCloseEditor);
79810 tooltip.select('.instruction').style('display', 'none');
79812 reveal('.entity-editor-pane', addNameString, {
79813 tooltipClass: 'intro-points-describe'
79817 context.history().on('change.intro', function () {
79818 continueTo(addCloseEditor);
79820 context.on('exit.intro', function () {
79821 // if user leaves select mode here, just continue with the tutorial.
79822 continueTo(reselectPoint);
79825 function continueTo(nextStep) {
79826 context.on('exit.intro', null);
79827 context.history().on('change.intro', null);
79832 function addCloseEditor() {
79833 // reset pane, in case user happened to change it..
79834 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79835 var selector = '.entity-editor-pane button.close svg use';
79836 var href = select(selector).attr('href') || '#iD-icon-close';
79837 context.on('exit.intro', function () {
79838 continueTo(reselectPoint);
79840 reveal('.entity-editor-pane', helpHtml('intro.points.add_close', {
79841 button: icon(href, 'inline')
79844 function continueTo(nextStep) {
79845 context.on('exit.intro', null);
79850 function reselectPoint() {
79851 if (!_pointID) return chapter.restart();
79852 var entity = context.hasEntity(_pointID);
79853 if (!entity) return chapter.restart(); // make sure it's still a cafe, in case user somehow changed it..
79855 var oldPreset = _mainPresetIndex.match(entity, context.graph());
79856 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
79857 context.enter(modeBrowse(context));
79858 var msec = transitionTime(entity.loc, context.map().center());
79861 reveal(null, null, {
79866 context.map().centerEase(entity.loc, msec);
79867 timeout(function () {
79868 var box = pointBox(entity.loc, context);
79869 reveal(box, helpHtml('intro.points.reselect'), {
79872 timeout(function () {
79873 context.map().on('move.intro drawn.intro', function () {
79874 var entity = context.hasEntity(_pointID);
79875 if (!entity) return chapter.restart();
79876 var box = pointBox(entity.loc, context);
79877 reveal(box, helpHtml('intro.points.reselect'), {
79881 }, 600); // after reveal..
79883 context.on('enter.intro', function (mode) {
79884 if (mode.id !== 'select') return;
79885 continueTo(updatePoint);
79889 function continueTo(nextStep) {
79890 context.map().on('move.intro drawn.intro', null);
79891 context.on('enter.intro', null);
79896 function updatePoint() {
79897 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79898 return continueTo(reselectPoint);
79899 } // reset pane, in case user happened to untag the point..
79902 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79903 context.on('exit.intro', function () {
79904 continueTo(reselectPoint);
79906 context.history().on('change.intro', function () {
79907 continueTo(updateCloseEditor);
79909 timeout(function () {
79910 reveal('.entity-editor-pane', helpHtml('intro.points.update'), {
79911 tooltipClass: 'intro-points-describe'
79915 function continueTo(nextStep) {
79916 context.on('exit.intro', null);
79917 context.history().on('change.intro', null);
79922 function updateCloseEditor() {
79923 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
79924 return continueTo(reselectPoint);
79925 } // reset pane, in case user happened to change it..
79928 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
79929 context.on('exit.intro', function () {
79930 continueTo(rightClickPoint);
79932 timeout(function () {
79933 reveal('.entity-editor-pane', helpHtml('intro.points.update_close', {
79934 button: icon('#iD-icon-close', 'inline')
79938 function continueTo(nextStep) {
79939 context.on('exit.intro', null);
79944 function rightClickPoint() {
79945 if (!_pointID) return chapter.restart();
79946 var entity = context.hasEntity(_pointID);
79947 if (!entity) return chapter.restart();
79948 context.enter(modeBrowse(context));
79949 var box = pointBox(entity.loc, context);
79950 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
79951 reveal(box, helpHtml('intro.points.' + textId), {
79954 timeout(function () {
79955 context.map().on('move.intro', function () {
79956 var entity = context.hasEntity(_pointID);
79957 if (!entity) return chapter.restart();
79958 var box = pointBox(entity.loc, context);
79959 reveal(box, helpHtml('intro.points.' + textId), {
79963 }, 600); // after reveal
79965 context.on('enter.intro', function (mode) {
79966 if (mode.id !== 'select') return;
79967 var ids = context.selectedIDs();
79968 if (ids.length !== 1 || ids[0] !== _pointID) return;
79969 timeout(function () {
79970 var node = selectMenuItem(context, 'delete').node();
79972 continueTo(enterDelete);
79973 }, 50); // after menu visible
79976 function continueTo(nextStep) {
79977 context.on('enter.intro', null);
79978 context.map().on('move.intro', null);
79983 function enterDelete() {
79984 if (!_pointID) return chapter.restart();
79985 var entity = context.hasEntity(_pointID);
79986 if (!entity) return chapter.restart();
79987 var node = selectMenuItem(context, 'delete').node();
79990 return continueTo(rightClickPoint);
79993 reveal('.edit-menu', helpHtml('intro.points.delete'), {
79996 timeout(function () {
79997 context.map().on('move.intro', function () {
79998 reveal('.edit-menu', helpHtml('intro.points.delete'), {
80003 }, 300); // after menu visible
80005 context.on('exit.intro', function () {
80006 if (!_pointID) return chapter.restart();
80007 var entity = context.hasEntity(_pointID);
80008 if (entity) return continueTo(rightClickPoint); // point still exists
80010 context.history().on('change.intro', function (changed) {
80011 if (changed.deleted().length) {
80016 function continueTo(nextStep) {
80017 context.map().on('move.intro', null);
80018 context.history().on('change.intro', null);
80019 context.on('exit.intro', null);
80025 context.history().on('change.intro', function () {
80028 reveal('.top-toolbar button.undo-button', helpHtml('intro.points.undo'));
80030 function continueTo(nextStep) {
80031 context.history().on('change.intro', null);
80037 dispatch$1.call('done');
80038 reveal('.ideditor', helpHtml('intro.points.play', {
80039 next: _t('intro.areas.title')
80041 tooltipBox: '.intro-nav-wrap .chapter-area',
80042 buttonText: _t.html('intro.ok'),
80043 buttonCallback: function buttonCallback() {
80044 reveal('.ideditor');
80049 chapter.enter = function () {
80053 chapter.exit = function () {
80054 timeouts.forEach(window.clearTimeout);
80055 context.on('enter.intro exit.intro', null);
80056 context.map().on('move.intro drawn.intro', null);
80057 context.history().on('change.intro', null);
80058 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80059 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80062 chapter.restart = function () {
80067 return utilRebind(chapter, dispatch$1, 'on');
80070 function uiIntroArea(context, reveal) {
80071 var dispatch$1 = dispatch('done');
80072 var playground = [-85.63552, 41.94159];
80073 var playgroundPreset = _mainPresetIndex.item('leisure/playground');
80074 var nameField = _mainPresetIndex.field('name');
80075 var descriptionField = _mainPresetIndex.field('description');
80081 title: 'intro.areas.title'
80084 function timeout(f, t) {
80085 timeouts.push(window.setTimeout(f, t));
80088 function eventCancel(d3_event) {
80089 d3_event.stopPropagation();
80090 d3_event.preventDefault();
80093 function revealPlayground(center, text, options) {
80094 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
80095 var box = pad(center, padding, context);
80096 reveal(box, text, options);
80099 function addArea() {
80100 context.enter(modeBrowse(context));
80101 context.history().reset('initial');
80103 var msec = transitionTime(playground, context.map().center());
80106 reveal(null, null, {
80111 context.map().centerZoomEase(playground, 19, msec);
80112 timeout(function () {
80113 var tooltip = reveal('button.add-area', helpHtml('intro.areas.add_playground'));
80114 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-areas');
80115 context.on('enter.intro', function (mode) {
80116 if (mode.id !== 'add-area') return;
80117 continueTo(startPlayground);
80121 function continueTo(nextStep) {
80122 context.on('enter.intro', null);
80127 function startPlayground() {
80128 if (context.mode().id !== 'add-area') {
80129 return chapter.restart();
80133 context.map().zoomEase(19.5, 500);
80134 timeout(function () {
80135 var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
80136 var startDrawString = helpHtml('intro.areas.start_playground') + helpHtml('intro.areas.' + textId);
80137 revealPlayground(playground, startDrawString, {
80140 timeout(function () {
80141 context.map().on('move.intro drawn.intro', function () {
80142 revealPlayground(playground, startDrawString, {
80146 context.on('enter.intro', function (mode) {
80147 if (mode.id !== 'draw-area') return chapter.restart();
80148 continueTo(continuePlayground);
80150 }, 250); // after reveal
80151 }, 550); // after easing
80153 function continueTo(nextStep) {
80154 context.map().on('move.intro drawn.intro', null);
80155 context.on('enter.intro', null);
80160 function continuePlayground() {
80161 if (context.mode().id !== 'draw-area') {
80162 return chapter.restart();
80166 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
80169 timeout(function () {
80170 context.map().on('move.intro drawn.intro', function () {
80171 revealPlayground(playground, helpHtml('intro.areas.continue_playground'), {
80175 }, 250); // after reveal
80177 context.on('enter.intro', function (mode) {
80178 if (mode.id === 'draw-area') {
80179 var entity = context.hasEntity(context.selectedIDs()[0]);
80181 if (entity && entity.nodes.length >= 6) {
80182 return continueTo(finishPlayground);
80186 } else if (mode.id === 'select') {
80187 _areaID = context.selectedIDs()[0];
80188 return continueTo(searchPresets);
80190 return chapter.restart();
80194 function continueTo(nextStep) {
80195 context.map().on('move.intro drawn.intro', null);
80196 context.on('enter.intro', null);
80201 function finishPlayground() {
80202 if (context.mode().id !== 'draw-area') {
80203 return chapter.restart();
80207 var finishString = helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.areas.finish_playground');
80208 revealPlayground(playground, finishString, {
80211 timeout(function () {
80212 context.map().on('move.intro drawn.intro', function () {
80213 revealPlayground(playground, finishString, {
80217 }, 250); // after reveal
80219 context.on('enter.intro', function (mode) {
80220 if (mode.id === 'draw-area') {
80222 } else if (mode.id === 'select') {
80223 _areaID = context.selectedIDs()[0];
80224 return continueTo(searchPresets);
80226 return chapter.restart();
80230 function continueTo(nextStep) {
80231 context.map().on('move.intro drawn.intro', null);
80232 context.on('enter.intro', null);
80237 function searchPresets() {
80238 if (!_areaID || !context.hasEntity(_areaID)) {
80242 var ids = context.selectedIDs();
80244 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80245 context.enter(modeSelect(context, [_areaID]));
80246 } // disallow scrolling
80249 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80250 timeout(function () {
80251 // reset pane, in case user somehow happened to change it..
80252 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80253 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
80254 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
80255 preset: playgroundPreset.name()
80257 }, 400); // after preset list pane visible..
80259 context.on('enter.intro', function (mode) {
80260 if (!_areaID || !context.hasEntity(_areaID)) {
80261 return continueTo(addArea);
80264 var ids = context.selectedIDs();
80266 if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
80267 // keep the user's area selected..
80268 context.enter(modeSelect(context, [_areaID])); // reset pane, in case user somehow happened to change it..
80270 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
80272 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80273 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
80274 reveal('.preset-search-input', helpHtml('intro.areas.search_playground', {
80275 preset: playgroundPreset.name()
80277 context.history().on('change.intro', null);
80281 function checkPresetSearch() {
80282 var first = context.container().select('.preset-list-item:first-child');
80284 if (first.classed('preset-leisure-playground')) {
80285 reveal(first.select('.preset-list-button').node(), helpHtml('intro.areas.choose_playground', {
80286 preset: playgroundPreset.name()
80290 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
80291 context.history().on('change.intro', function () {
80292 continueTo(clickAddField);
80297 function continueTo(nextStep) {
80298 context.container().select('.inspector-wrap').on('wheel.intro', null);
80299 context.on('enter.intro', null);
80300 context.history().on('change.intro', null);
80301 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80306 function clickAddField() {
80307 if (!_areaID || !context.hasEntity(_areaID)) {
80311 var ids = context.selectedIDs();
80313 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80314 return searchPresets();
80317 if (!context.container().select('.form-field-description').empty()) {
80318 return continueTo(describePlayground);
80319 } // disallow scrolling
80322 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80323 timeout(function () {
80324 // reset pane, in case user somehow happened to change it..
80325 context.container().select('.inspector-wrap .panewrap').style('right', '0%'); // It's possible for the user to add a description in a previous step..
80326 // If they did this already, just continue to next step.
80328 var entity = context.entity(_areaID);
80330 if (entity.tags.description) {
80331 return continueTo(play);
80332 } // scroll "Add field" into view
80335 var box = context.container().select('.more-fields').node().getBoundingClientRect();
80337 if (box.top > 300) {
80338 var pane = context.container().select('.entity-editor-pane .inspector-body');
80339 var start = pane.node().scrollTop;
80340 var end = start + (box.top - 300);
80341 pane.transition().duration(250).tween('scroll.inspector', function () {
80343 var i = d3_interpolateNumber(start, end);
80344 return function (t) {
80345 node.scrollTop = i(t);
80350 timeout(function () {
80351 reveal('.more-fields .combobox-input', helpHtml('intro.areas.add_field', {
80352 name: nameField.label(),
80353 description: descriptionField.label()
80357 context.container().select('.more-fields .combobox-input').on('click.intro', function () {
80358 // Watch for the combobox to appear...
80360 watcher = window.setInterval(function () {
80361 if (!context.container().select('div.combobox').empty()) {
80362 window.clearInterval(watcher);
80363 continueTo(chooseDescriptionField);
80367 }, 300); // after "Add Field" visible
80368 }, 400); // after editor pane visible
80370 context.on('exit.intro', function () {
80371 return continueTo(searchPresets);
80374 function continueTo(nextStep) {
80375 context.container().select('.inspector-wrap').on('wheel.intro', null);
80376 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80377 context.on('exit.intro', null);
80382 function chooseDescriptionField() {
80383 if (!_areaID || !context.hasEntity(_areaID)) {
80387 var ids = context.selectedIDs();
80389 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80390 return searchPresets();
80393 if (!context.container().select('.form-field-description').empty()) {
80394 return continueTo(describePlayground);
80395 } // Make sure combobox is ready..
80398 if (context.container().select('div.combobox').empty()) {
80399 return continueTo(clickAddField);
80400 } // Watch for the combobox to go away..
80404 watcher = window.setInterval(function () {
80405 if (context.container().select('div.combobox').empty()) {
80406 window.clearInterval(watcher);
80407 timeout(function () {
80408 if (context.container().select('.form-field-description').empty()) {
80409 continueTo(retryChooseDescription);
80411 continueTo(describePlayground);
80413 }, 300); // after description field added.
80416 reveal('div.combobox', helpHtml('intro.areas.choose_field', {
80417 field: descriptionField.label()
80421 context.on('exit.intro', function () {
80422 return continueTo(searchPresets);
80425 function continueTo(nextStep) {
80426 if (watcher) window.clearInterval(watcher);
80427 context.on('exit.intro', null);
80432 function describePlayground() {
80433 if (!_areaID || !context.hasEntity(_areaID)) {
80437 var ids = context.selectedIDs();
80439 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80440 return searchPresets();
80441 } // reset pane, in case user happened to change it..
80444 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
80446 if (context.container().select('.form-field-description').empty()) {
80447 return continueTo(retryChooseDescription);
80450 context.on('exit.intro', function () {
80453 reveal('.entity-editor-pane', helpHtml('intro.areas.describe_playground', {
80454 button: icon('#iD-icon-close', 'inline')
80459 function continueTo(nextStep) {
80460 context.on('exit.intro', null);
80465 function retryChooseDescription() {
80466 if (!_areaID || !context.hasEntity(_areaID)) {
80470 var ids = context.selectedIDs();
80472 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
80473 return searchPresets();
80474 } // reset pane, in case user happened to change it..
80477 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
80478 reveal('.entity-editor-pane', helpHtml('intro.areas.retry_add_field', {
80479 field: descriptionField.label()
80481 buttonText: _t.html('intro.ok'),
80482 buttonCallback: function buttonCallback() {
80483 continueTo(clickAddField);
80486 context.on('exit.intro', function () {
80487 return continueTo(searchPresets);
80490 function continueTo(nextStep) {
80491 context.on('exit.intro', null);
80497 dispatch$1.call('done');
80498 reveal('.ideditor', helpHtml('intro.areas.play', {
80499 next: _t('intro.lines.title')
80501 tooltipBox: '.intro-nav-wrap .chapter-line',
80502 buttonText: _t.html('intro.ok'),
80503 buttonCallback: function buttonCallback() {
80504 reveal('.ideditor');
80509 chapter.enter = function () {
80513 chapter.exit = function () {
80514 timeouts.forEach(window.clearTimeout);
80515 context.on('enter.intro exit.intro', null);
80516 context.map().on('move.intro drawn.intro', null);
80517 context.history().on('change.intro', null);
80518 context.container().select('.inspector-wrap').on('wheel.intro', null);
80519 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80520 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80523 chapter.restart = function () {
80528 return utilRebind(chapter, dispatch$1, 'on');
80531 function uiIntroLine(context, reveal) {
80532 var dispatch$1 = dispatch('done');
80534 var _tulipRoadID = null;
80535 var flowerRoadID = 'w646';
80536 var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
80537 var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
80538 var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
80539 var roadCategory = _mainPresetIndex.item('category-road_minor');
80540 var residentialPreset = _mainPresetIndex.item('highway/residential');
80541 var woodRoadID = 'w525';
80542 var woodRoadEndID = 'n2862';
80543 var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
80544 var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
80545 var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
80546 var washingtonStreetID = 'w522';
80547 var twelfthAvenueID = 'w1';
80548 var eleventhAvenueEndID = 'n3550';
80549 var twelfthAvenueEndID = 'n5';
80550 var _washingtonSegmentID = null;
80551 var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
80552 var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
80553 var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
80554 var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
80556 title: 'intro.lines.title'
80559 function timeout(f, t) {
80560 timeouts.push(window.setTimeout(f, t));
80563 function eventCancel(d3_event) {
80564 d3_event.stopPropagation();
80565 d3_event.preventDefault();
80568 function addLine() {
80569 context.enter(modeBrowse(context));
80570 context.history().reset('initial');
80571 var msec = transitionTime(tulipRoadStart, context.map().center());
80574 reveal(null, null, {
80579 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
80580 timeout(function () {
80581 var tooltip = reveal('button.add-line', helpHtml('intro.lines.add_line'));
80582 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-lines');
80583 context.on('enter.intro', function (mode) {
80584 if (mode.id !== 'add-line') return;
80585 continueTo(startLine);
80589 function continueTo(nextStep) {
80590 context.on('enter.intro', null);
80595 function startLine() {
80596 if (context.mode().id !== 'add-line') return chapter.restart();
80597 _tulipRoadID = null;
80598 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
80599 var box = pad(tulipRoadStart, padding, context);
80600 box.height = box.height + 100;
80601 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
80602 var startLineString = helpHtml('intro.lines.missing_road') + '{br}' + helpHtml('intro.lines.line_draw_info') + helpHtml('intro.lines.' + textId);
80603 reveal(box, startLineString);
80604 context.map().on('move.intro drawn.intro', function () {
80605 padding = 70 * Math.pow(2, context.map().zoom() - 18);
80606 box = pad(tulipRoadStart, padding, context);
80607 box.height = box.height + 100;
80608 reveal(box, startLineString, {
80612 context.on('enter.intro', function (mode) {
80613 if (mode.id !== 'draw-line') return chapter.restart();
80614 continueTo(drawLine);
80617 function continueTo(nextStep) {
80618 context.map().on('move.intro drawn.intro', null);
80619 context.on('enter.intro', null);
80624 function drawLine() {
80625 if (context.mode().id !== 'draw-line') return chapter.restart();
80626 _tulipRoadID = context.mode().selectedIDs()[0];
80627 context.map().centerEase(tulipRoadMidpoint, 500);
80628 timeout(function () {
80629 var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
80630 var box = pad(tulipRoadMidpoint, padding, context);
80631 box.height = box.height * 2;
80632 reveal(box, helpHtml('intro.lines.intersect', {
80633 name: _t('intro.graph.name.flower-street')
80635 context.map().on('move.intro drawn.intro', function () {
80636 padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
80637 box = pad(tulipRoadMidpoint, padding, context);
80638 box.height = box.height * 2;
80639 reveal(box, helpHtml('intro.lines.intersect', {
80640 name: _t('intro.graph.name.flower-street')
80645 }, 550); // after easing..
80647 context.history().on('change.intro', function () {
80648 if (isLineConnected()) {
80649 continueTo(continueLine);
80652 context.on('enter.intro', function (mode) {
80653 if (mode.id === 'draw-line') {
80655 } else if (mode.id === 'select') {
80656 continueTo(retryIntersect);
80659 return chapter.restart();
80663 function continueTo(nextStep) {
80664 context.map().on('move.intro drawn.intro', null);
80665 context.history().on('change.intro', null);
80666 context.on('enter.intro', null);
80671 function isLineConnected() {
80672 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
80674 if (!entity) return false;
80675 var drawNodes = context.graph().childNodes(entity);
80676 return drawNodes.some(function (node) {
80677 return context.graph().parentWays(node).some(function (parent) {
80678 return parent.id === flowerRoadID;
80683 function retryIntersect() {
80684 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
80685 var box = pad(tulipRoadIntersection, 80, context);
80686 reveal(box, helpHtml('intro.lines.retry_intersect', {
80687 name: _t('intro.graph.name.flower-street')
80689 timeout(chapter.restart, 3000);
80692 function continueLine() {
80693 if (context.mode().id !== 'draw-line') return chapter.restart();
80695 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
80697 if (!entity) return chapter.restart();
80698 context.map().centerEase(tulipRoadIntersection, 500);
80699 var continueLineText = helpHtml('intro.lines.continue_line') + '{br}' + helpHtml('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.lines.finish_road');
80700 reveal('.surface', continueLineText);
80701 context.on('enter.intro', function (mode) {
80702 if (mode.id === 'draw-line') return;else if (mode.id === 'select') return continueTo(chooseCategoryRoad);else return chapter.restart();
80705 function continueTo(nextStep) {
80706 context.on('enter.intro', null);
80711 function chooseCategoryRoad() {
80712 if (context.mode().id !== 'select') return chapter.restart();
80713 context.on('exit.intro', function () {
80714 return chapter.restart();
80716 var button = context.container().select('.preset-category-road_minor .preset-list-button');
80717 if (button.empty()) return chapter.restart(); // disallow scrolling
80719 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80720 timeout(function () {
80721 // reset pane, in case user somehow happened to change it..
80722 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80723 reveal(button.node(), helpHtml('intro.lines.choose_category_road', {
80724 category: roadCategory.name()
80726 button.on('click.intro', function () {
80727 continueTo(choosePresetResidential);
80729 }, 400); // after editor pane visible
80731 function continueTo(nextStep) {
80732 context.container().select('.inspector-wrap').on('wheel.intro', null);
80733 context.container().select('.preset-list-button').on('click.intro', null);
80734 context.on('exit.intro', null);
80739 function choosePresetResidential() {
80740 if (context.mode().id !== 'select') return chapter.restart();
80741 context.on('exit.intro', function () {
80742 return chapter.restart();
80744 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
80745 if (subgrid.empty()) return chapter.restart();
80746 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button').on('click.intro', function () {
80747 continueTo(retryPresetResidential);
80749 subgrid.selectAll('.preset-highway-residential .preset-list-button').on('click.intro', function () {
80750 continueTo(nameRoad);
80752 timeout(function () {
80753 reveal(subgrid.node(), helpHtml('intro.lines.choose_preset_residential', {
80754 preset: residentialPreset.name()
80756 tooltipBox: '.preset-highway-residential .preset-list-button',
80761 function continueTo(nextStep) {
80762 context.container().select('.preset-list-button').on('click.intro', null);
80763 context.on('exit.intro', null);
80766 } // selected wrong road type
80769 function retryPresetResidential() {
80770 if (context.mode().id !== 'select') return chapter.restart();
80771 context.on('exit.intro', function () {
80772 return chapter.restart();
80773 }); // disallow scrolling
80775 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80776 timeout(function () {
80777 var button = context.container().select('.entity-editor-pane .preset-list-button');
80778 reveal(button.node(), helpHtml('intro.lines.retry_preset_residential', {
80779 preset: residentialPreset.name()
80781 button.on('click.intro', function () {
80782 continueTo(chooseCategoryRoad);
80786 function continueTo(nextStep) {
80787 context.container().select('.inspector-wrap').on('wheel.intro', null);
80788 context.container().select('.preset-list-button').on('click.intro', null);
80789 context.on('exit.intro', null);
80794 function nameRoad() {
80795 context.on('exit.intro', function () {
80796 continueTo(didNameRoad);
80798 timeout(function () {
80799 reveal('.entity-editor-pane', helpHtml('intro.lines.name_road', {
80800 button: icon('#iD-icon-close', 'inline')
80802 tooltipClass: 'intro-lines-name_road'
80806 function continueTo(nextStep) {
80807 context.on('exit.intro', null);
80812 function didNameRoad() {
80813 context.history().checkpoint('doneAddLine');
80814 timeout(function () {
80815 reveal('.surface', helpHtml('intro.lines.did_name_road'), {
80816 buttonText: _t.html('intro.ok'),
80817 buttonCallback: function buttonCallback() {
80818 continueTo(updateLine);
80823 function continueTo(nextStep) {
80828 function updateLine() {
80829 context.history().reset('doneAddLine');
80831 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80832 return chapter.restart();
80835 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
80838 reveal(null, null, {
80843 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
80844 timeout(function () {
80845 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
80846 var box = pad(woodRoadDragMidpoint, padding, context);
80848 var advance = function advance() {
80849 continueTo(addNode);
80852 reveal(box, helpHtml('intro.lines.update_line'), {
80853 buttonText: _t.html('intro.ok'),
80854 buttonCallback: advance
80856 context.map().on('move.intro drawn.intro', function () {
80857 var padding = 250 * Math.pow(2, context.map().zoom() - 19);
80858 var box = pad(woodRoadDragMidpoint, padding, context);
80859 reveal(box, helpHtml('intro.lines.update_line'), {
80861 buttonText: _t.html('intro.ok'),
80862 buttonCallback: advance
80867 function continueTo(nextStep) {
80868 context.map().on('move.intro drawn.intro', null);
80873 function addNode() {
80874 context.history().reset('doneAddLine');
80876 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80877 return chapter.restart();
80880 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
80881 var box = pad(woodRoadAddNode, padding, context);
80882 var addNodeString = helpHtml('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
80883 reveal(box, addNodeString);
80884 context.map().on('move.intro drawn.intro', function () {
80885 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
80886 var box = pad(woodRoadAddNode, padding, context);
80887 reveal(box, addNodeString, {
80891 context.history().on('change.intro', function (changed) {
80892 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80893 return continueTo(updateLine);
80896 if (changed.created().length === 1) {
80897 timeout(function () {
80898 continueTo(startDragEndpoint);
80902 context.on('enter.intro', function (mode) {
80903 if (mode.id !== 'select') {
80904 continueTo(updateLine);
80908 function continueTo(nextStep) {
80909 context.map().on('move.intro drawn.intro', null);
80910 context.history().on('change.intro', null);
80911 context.on('enter.intro', null);
80916 function startDragEndpoint() {
80917 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80918 return continueTo(updateLine);
80921 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80922 var box = pad(woodRoadDragEndpoint, padding, context);
80923 var startDragString = helpHtml('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) + helpHtml('intro.lines.drag_to_intersection');
80924 reveal(box, startDragString);
80925 context.map().on('move.intro drawn.intro', function () {
80926 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80927 return continueTo(updateLine);
80930 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80931 var box = pad(woodRoadDragEndpoint, padding, context);
80932 reveal(box, startDragString, {
80935 var entity = context.entity(woodRoadEndID);
80937 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
80938 continueTo(finishDragEndpoint);
80942 function continueTo(nextStep) {
80943 context.map().on('move.intro drawn.intro', null);
80948 function finishDragEndpoint() {
80949 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80950 return continueTo(updateLine);
80953 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80954 var box = pad(woodRoadDragEndpoint, padding, context);
80955 var finishDragString = helpHtml('intro.lines.spot_looks_good') + helpHtml('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
80956 reveal(box, finishDragString);
80957 context.map().on('move.intro drawn.intro', function () {
80958 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80959 return continueTo(updateLine);
80962 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
80963 var box = pad(woodRoadDragEndpoint, padding, context);
80964 reveal(box, finishDragString, {
80967 var entity = context.entity(woodRoadEndID);
80969 if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
80970 continueTo(startDragEndpoint);
80973 context.on('enter.intro', function () {
80974 continueTo(startDragMidpoint);
80977 function continueTo(nextStep) {
80978 context.map().on('move.intro drawn.intro', null);
80979 context.on('enter.intro', null);
80984 function startDragMidpoint() {
80985 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80986 return continueTo(updateLine);
80989 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
80990 context.enter(modeSelect(context, [woodRoadID]));
80993 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
80994 var box = pad(woodRoadDragMidpoint, padding, context);
80995 reveal(box, helpHtml('intro.lines.start_drag_midpoint'));
80996 context.map().on('move.intro drawn.intro', function () {
80997 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
80998 return continueTo(updateLine);
81001 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
81002 var box = pad(woodRoadDragMidpoint, padding, context);
81003 reveal(box, helpHtml('intro.lines.start_drag_midpoint'), {
81007 context.history().on('change.intro', function (changed) {
81008 if (changed.created().length === 1) {
81009 continueTo(continueDragMidpoint);
81012 context.on('enter.intro', function (mode) {
81013 if (mode.id !== 'select') {
81014 // keep Wood Road selected so midpoint triangles are drawn..
81015 context.enter(modeSelect(context, [woodRoadID]));
81019 function continueTo(nextStep) {
81020 context.map().on('move.intro drawn.intro', null);
81021 context.history().on('change.intro', null);
81022 context.on('enter.intro', null);
81027 function continueDragMidpoint() {
81028 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
81029 return continueTo(updateLine);
81032 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
81033 var box = pad(woodRoadDragEndpoint, padding, context);
81036 var advance = function advance() {
81037 context.history().checkpoint('doneUpdateLine');
81038 continueTo(deleteLines);
81041 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
81042 buttonText: _t.html('intro.ok'),
81043 buttonCallback: advance
81045 context.map().on('move.intro drawn.intro', function () {
81046 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
81047 return continueTo(updateLine);
81050 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
81051 var box = pad(woodRoadDragEndpoint, padding, context);
81053 reveal(box, helpHtml('intro.lines.continue_drag_midpoint'), {
81055 buttonText: _t.html('intro.ok'),
81056 buttonCallback: advance
81060 function continueTo(nextStep) {
81061 context.map().on('move.intro drawn.intro', null);
81066 function deleteLines() {
81067 context.history().reset('doneUpdateLine');
81068 context.enter(modeBrowse(context));
81070 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81071 return chapter.restart();
81074 var msec = transitionTime(deleteLinesLoc, context.map().center());
81077 reveal(null, null, {
81082 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
81083 timeout(function () {
81084 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81085 var box = pad(deleteLinesLoc, padding, context);
81089 var advance = function advance() {
81090 continueTo(rightClickIntersection);
81093 reveal(box, helpHtml('intro.lines.delete_lines', {
81094 street: _t('intro.graph.name.12th-avenue')
81096 buttonText: _t.html('intro.ok'),
81097 buttonCallback: advance
81099 context.map().on('move.intro drawn.intro', function () {
81100 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81101 var box = pad(deleteLinesLoc, padding, context);
81104 reveal(box, helpHtml('intro.lines.delete_lines', {
81105 street: _t('intro.graph.name.12th-avenue')
81108 buttonText: _t.html('intro.ok'),
81109 buttonCallback: advance
81112 context.history().on('change.intro', function () {
81113 timeout(function () {
81114 continueTo(deleteLines);
81115 }, 500); // after any transition (e.g. if user deleted intersection)
81119 function continueTo(nextStep) {
81120 context.map().on('move.intro drawn.intro', null);
81121 context.history().on('change.intro', null);
81126 function rightClickIntersection() {
81127 context.history().reset('doneUpdateLine');
81128 context.enter(modeBrowse(context));
81129 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
81130 var rightClickString = helpHtml('intro.lines.split_street', {
81131 street1: _t('intro.graph.name.11th-avenue'),
81132 street2: _t('intro.graph.name.washington-street')
81133 }) + helpHtml('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
81134 timeout(function () {
81135 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81136 var box = pad(eleventhAvenueEnd, padding, context);
81137 reveal(box, rightClickString);
81138 context.map().on('move.intro drawn.intro', function () {
81139 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81140 var box = pad(eleventhAvenueEnd, padding, context);
81141 reveal(box, rightClickString, {
81145 context.on('enter.intro', function (mode) {
81146 if (mode.id !== 'select') return;
81147 var ids = context.selectedIDs();
81148 if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) return;
81149 timeout(function () {
81150 var node = selectMenuItem(context, 'split').node();
81152 continueTo(splitIntersection);
81153 }, 50); // after menu visible
81155 context.history().on('change.intro', function () {
81156 timeout(function () {
81157 continueTo(deleteLines);
81158 }, 300); // after any transition (e.g. if user deleted intersection)
81162 function continueTo(nextStep) {
81163 context.map().on('move.intro drawn.intro', null);
81164 context.on('enter.intro', null);
81165 context.history().on('change.intro', null);
81170 function splitIntersection() {
81171 if (!context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81172 return continueTo(deleteLines);
81175 var node = selectMenuItem(context, 'split').node();
81178 return continueTo(rightClickIntersection);
81181 var wasChanged = false;
81182 _washingtonSegmentID = null;
81183 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
81184 street: _t('intro.graph.name.washington-street')
81188 context.map().on('move.intro drawn.intro', function () {
81189 var node = selectMenuItem(context, 'split').node();
81191 if (!wasChanged && !node) {
81192 return continueTo(rightClickIntersection);
81195 reveal('.edit-menu', helpHtml('intro.lines.split_intersection', {
81196 street: _t('intro.graph.name.washington-street')
81202 context.history().on('change.intro', function (changed) {
81204 timeout(function () {
81205 if (context.history().undoAnnotation() === _t('operations.split.annotation.line', {
81208 _washingtonSegmentID = changed.created()[0].id;
81209 continueTo(didSplit);
81211 _washingtonSegmentID = null;
81212 continueTo(retrySplit);
81214 }, 300); // after any transition (e.g. if user deleted intersection)
81217 function continueTo(nextStep) {
81218 context.map().on('move.intro drawn.intro', null);
81219 context.history().on('change.intro', null);
81224 function retrySplit() {
81225 context.enter(modeBrowse(context));
81226 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
81228 var advance = function advance() {
81229 continueTo(rightClickIntersection);
81232 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81233 var box = pad(eleventhAvenueEnd, padding, context);
81234 reveal(box, helpHtml('intro.lines.retry_split'), {
81235 buttonText: _t.html('intro.ok'),
81236 buttonCallback: advance
81238 context.map().on('move.intro drawn.intro', function () {
81239 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
81240 var box = pad(eleventhAvenueEnd, padding, context);
81241 reveal(box, helpHtml('intro.lines.retry_split'), {
81243 buttonText: _t.html('intro.ok'),
81244 buttonCallback: advance
81248 function continueTo(nextStep) {
81249 context.map().on('move.intro drawn.intro', null);
81254 function didSplit() {
81255 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81256 return continueTo(rightClickIntersection);
81259 var ids = context.selectedIDs();
81260 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
81261 var street = _t('intro.graph.name.washington-street');
81262 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81263 var box = pad(twelfthAvenue, padding, context);
81264 box.width = box.width / 2;
81265 reveal(box, helpHtml(string, {
81271 timeout(function () {
81272 context.map().centerZoomEase(twelfthAvenue, 18, 500);
81273 context.map().on('move.intro drawn.intro', function () {
81274 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81275 var box = pad(twelfthAvenue, padding, context);
81276 box.width = box.width / 2;
81277 reveal(box, helpHtml(string, {
81284 }, 600); // after initial reveal and curtain cut
81286 context.on('enter.intro', function () {
81287 var ids = context.selectedIDs();
81289 if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
81290 continueTo(multiSelect);
81293 context.history().on('change.intro', function () {
81294 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81295 return continueTo(rightClickIntersection);
81299 function continueTo(nextStep) {
81300 context.map().on('move.intro drawn.intro', null);
81301 context.on('enter.intro', null);
81302 context.history().on('change.intro', null);
81307 function multiSelect() {
81308 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81309 return continueTo(rightClickIntersection);
81312 var ids = context.selectedIDs();
81313 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
81314 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
81316 if (hasWashington && hasTwelfth) {
81317 return continueTo(multiRightClick);
81318 } else if (!hasWashington && !hasTwelfth) {
81319 return continueTo(didSplit);
81322 context.map().centerZoomEase(twelfthAvenue, 18, 500);
81323 timeout(function () {
81324 var selected, other, padding, box;
81326 if (hasWashington) {
81327 selected = _t('intro.graph.name.washington-street');
81328 other = _t('intro.graph.name.12th-avenue');
81329 padding = 60 * Math.pow(2, context.map().zoom() - 18);
81330 box = pad(twelfthAvenueEnd, padding, context);
81333 selected = _t('intro.graph.name.12th-avenue');
81334 other = _t('intro.graph.name.washington-street');
81335 padding = 200 * Math.pow(2, context.map().zoom() - 18);
81336 box = pad(twelfthAvenue, padding, context);
81340 reveal(box, helpHtml('intro.lines.multi_select', {
81341 selected: selected,
81343 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
81344 selected: selected,
81347 context.map().on('move.intro drawn.intro', function () {
81348 if (hasWashington) {
81349 selected = _t('intro.graph.name.washington-street');
81350 other = _t('intro.graph.name.12th-avenue');
81351 padding = 60 * Math.pow(2, context.map().zoom() - 18);
81352 box = pad(twelfthAvenueEnd, padding, context);
81355 selected = _t('intro.graph.name.12th-avenue');
81356 other = _t('intro.graph.name.washington-street');
81357 padding = 200 * Math.pow(2, context.map().zoom() - 18);
81358 box = pad(twelfthAvenue, padding, context);
81362 reveal(box, helpHtml('intro.lines.multi_select', {
81363 selected: selected,
81365 }) + ' ' + helpHtml('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'), {
81366 selected: selected,
81372 context.on('enter.intro', function () {
81373 continueTo(multiSelect);
81375 context.history().on('change.intro', function () {
81376 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81377 return continueTo(rightClickIntersection);
81382 function continueTo(nextStep) {
81383 context.map().on('move.intro drawn.intro', null);
81384 context.on('enter.intro', null);
81385 context.history().on('change.intro', null);
81390 function multiRightClick() {
81391 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81392 return continueTo(rightClickIntersection);
81395 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81396 var box = pad(twelfthAvenue, padding, context);
81397 var rightClickString = helpHtml('intro.lines.multi_select_success') + helpHtml('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
81398 reveal(box, rightClickString);
81399 context.map().on('move.intro drawn.intro', function () {
81400 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81401 var box = pad(twelfthAvenue, padding, context);
81402 reveal(box, rightClickString, {
81406 context.ui().editMenu().on('toggled.intro', function (open) {
81408 timeout(function () {
81409 var ids = context.selectedIDs();
81411 if (ids.length === 2 && ids.indexOf(twelfthAvenueID) !== -1 && ids.indexOf(_washingtonSegmentID) !== -1) {
81412 var node = selectMenuItem(context, 'delete').node();
81414 continueTo(multiDelete);
81415 } else if (ids.length === 1 && ids.indexOf(_washingtonSegmentID) !== -1) {
81416 return continueTo(multiSelect);
81418 return continueTo(didSplit);
81420 }, 300); // after edit menu visible
81422 context.history().on('change.intro', function () {
81423 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81424 return continueTo(rightClickIntersection);
81428 function continueTo(nextStep) {
81429 context.map().on('move.intro drawn.intro', null);
81430 context.ui().editMenu().on('toggled.intro', null);
81431 context.history().on('change.intro', null);
81436 function multiDelete() {
81437 if (!_washingtonSegmentID || !context.hasEntity(_washingtonSegmentID) || !context.hasEntity(washingtonStreetID) || !context.hasEntity(twelfthAvenueID) || !context.hasEntity(eleventhAvenueEndID)) {
81438 return continueTo(rightClickIntersection);
81441 var node = selectMenuItem(context, 'delete').node();
81442 if (!node) return continueTo(multiRightClick);
81443 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
81446 context.map().on('move.intro drawn.intro', function () {
81447 reveal('.edit-menu', helpHtml('intro.lines.multi_delete'), {
81452 context.on('exit.intro', function () {
81453 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
81454 return continueTo(multiSelect); // left select mode but roads still exist
81457 context.history().on('change.intro', function () {
81458 if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
81459 continueTo(retryDelete); // changed something but roads still exist
81465 function continueTo(nextStep) {
81466 context.map().on('move.intro drawn.intro', null);
81467 context.on('exit.intro', null);
81468 context.history().on('change.intro', null);
81473 function retryDelete() {
81474 context.enter(modeBrowse(context));
81475 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
81476 var box = pad(twelfthAvenue, padding, context);
81477 reveal(box, helpHtml('intro.lines.retry_delete'), {
81478 buttonText: _t.html('intro.ok'),
81479 buttonCallback: function buttonCallback() {
81480 continueTo(multiSelect);
81484 function continueTo(nextStep) {
81490 dispatch$1.call('done');
81491 reveal('.ideditor', helpHtml('intro.lines.play', {
81492 next: _t('intro.buildings.title')
81494 tooltipBox: '.intro-nav-wrap .chapter-building',
81495 buttonText: _t.html('intro.ok'),
81496 buttonCallback: function buttonCallback() {
81497 reveal('.ideditor');
81502 chapter.enter = function () {
81506 chapter.exit = function () {
81507 timeouts.forEach(window.clearTimeout);
81508 select(window).on('pointerdown.intro mousedown.intro', null, true);
81509 context.on('enter.intro exit.intro', null);
81510 context.map().on('move.intro drawn.intro', null);
81511 context.history().on('change.intro', null);
81512 context.container().select('.inspector-wrap').on('wheel.intro', null);
81513 context.container().select('.preset-list-button').on('click.intro', null);
81516 chapter.restart = function () {
81521 return utilRebind(chapter, dispatch$1, 'on');
81524 function uiIntroBuilding(context, reveal) {
81525 var dispatch$1 = dispatch('done');
81526 var house = [-85.62815, 41.95638];
81527 var tank = [-85.62732, 41.95347];
81528 var buildingCatetory = _mainPresetIndex.item('category-building');
81529 var housePreset = _mainPresetIndex.item('building/house');
81530 var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
81532 var _houseID = null;
81533 var _tankID = null;
81535 title: 'intro.buildings.title'
81538 function timeout(f, t) {
81539 timeouts.push(window.setTimeout(f, t));
81542 function eventCancel(d3_event) {
81543 d3_event.stopPropagation();
81544 d3_event.preventDefault();
81547 function revealHouse(center, text, options) {
81548 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
81549 var box = pad(center, padding, context);
81550 reveal(box, text, options);
81553 function revealTank(center, text, options) {
81554 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
81555 var box = pad(center, padding, context);
81556 reveal(box, text, options);
81559 function addHouse() {
81560 context.enter(modeBrowse(context));
81561 context.history().reset('initial');
81563 var msec = transitionTime(house, context.map().center());
81566 reveal(null, null, {
81571 context.map().centerZoomEase(house, 19, msec);
81572 timeout(function () {
81573 var tooltip = reveal('button.add-area', helpHtml('intro.buildings.add_building'));
81574 tooltip.selectAll('.popover-inner').insert('svg', 'span').attr('class', 'tooltip-illustration').append('use').attr('xlink:href', '#iD-graphic-buildings');
81575 context.on('enter.intro', function (mode) {
81576 if (mode.id !== 'add-area') return;
81577 continueTo(startHouse);
81581 function continueTo(nextStep) {
81582 context.on('enter.intro', null);
81587 function startHouse() {
81588 if (context.mode().id !== 'add-area') {
81589 return continueTo(addHouse);
81593 context.map().zoomEase(20, 500);
81594 timeout(function () {
81595 var startString = helpHtml('intro.buildings.start_building') + helpHtml('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
81596 revealHouse(house, startString);
81597 context.map().on('move.intro drawn.intro', function () {
81598 revealHouse(house, startString, {
81602 context.on('enter.intro', function (mode) {
81603 if (mode.id !== 'draw-area') return chapter.restart();
81604 continueTo(continueHouse);
81606 }, 550); // after easing
81608 function continueTo(nextStep) {
81609 context.map().on('move.intro drawn.intro', null);
81610 context.on('enter.intro', null);
81615 function continueHouse() {
81616 if (context.mode().id !== 'draw-area') {
81617 return continueTo(addHouse);
81621 var continueString = helpHtml('intro.buildings.continue_building') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_building');
81622 revealHouse(house, continueString);
81623 context.map().on('move.intro drawn.intro', function () {
81624 revealHouse(house, continueString, {
81628 context.on('enter.intro', function (mode) {
81629 if (mode.id === 'draw-area') {
81631 } else if (mode.id === 'select') {
81632 var graph = context.graph();
81633 var way = context.entity(context.selectedIDs()[0]);
81634 var nodes = graph.childNodes(way);
81635 var points = utilArrayUniq(nodes).map(function (n) {
81636 return context.projection(n.loc);
81639 if (isMostlySquare(points)) {
81641 return continueTo(chooseCategoryBuilding);
81643 return continueTo(retryHouse);
81646 return chapter.restart();
81650 function continueTo(nextStep) {
81651 context.map().on('move.intro drawn.intro', null);
81652 context.on('enter.intro', null);
81657 function retryHouse() {
81658 var onClick = function onClick() {
81659 continueTo(addHouse);
81662 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
81663 buttonText: _t.html('intro.ok'),
81664 buttonCallback: onClick
81666 context.map().on('move.intro drawn.intro', function () {
81667 revealHouse(house, helpHtml('intro.buildings.retry_building'), {
81669 buttonText: _t.html('intro.ok'),
81670 buttonCallback: onClick
81674 function continueTo(nextStep) {
81675 context.map().on('move.intro drawn.intro', null);
81680 function chooseCategoryBuilding() {
81681 if (!_houseID || !context.hasEntity(_houseID)) {
81685 var ids = context.selectedIDs();
81687 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81688 context.enter(modeSelect(context, [_houseID]));
81689 } // disallow scrolling
81692 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81693 timeout(function () {
81694 // reset pane, in case user somehow happened to change it..
81695 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
81696 var button = context.container().select('.preset-category-building .preset-list-button');
81697 reveal(button.node(), helpHtml('intro.buildings.choose_category_building', {
81698 category: buildingCatetory.name()
81700 button.on('click.intro', function () {
81701 button.on('click.intro', null);
81702 continueTo(choosePresetHouse);
81704 }, 400); // after preset list pane visible..
81706 context.on('enter.intro', function (mode) {
81707 if (!_houseID || !context.hasEntity(_houseID)) {
81708 return continueTo(addHouse);
81711 var ids = context.selectedIDs();
81713 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
81714 return continueTo(chooseCategoryBuilding);
81718 function continueTo(nextStep) {
81719 context.container().select('.inspector-wrap').on('wheel.intro', null);
81720 context.container().select('.preset-list-button').on('click.intro', null);
81721 context.on('enter.intro', null);
81726 function choosePresetHouse() {
81727 if (!_houseID || !context.hasEntity(_houseID)) {
81731 var ids = context.selectedIDs();
81733 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81734 context.enter(modeSelect(context, [_houseID]));
81735 } // disallow scrolling
81738 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
81739 timeout(function () {
81740 // reset pane, in case user somehow happened to change it..
81741 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
81742 var button = context.container().select('.preset-building-house .preset-list-button');
81743 reveal(button.node(), helpHtml('intro.buildings.choose_preset_house', {
81744 preset: housePreset.name()
81748 button.on('click.intro', function () {
81749 button.on('click.intro', null);
81750 continueTo(closeEditorHouse);
81752 }, 400); // after preset list pane visible..
81754 context.on('enter.intro', function (mode) {
81755 if (!_houseID || !context.hasEntity(_houseID)) {
81756 return continueTo(addHouse);
81759 var ids = context.selectedIDs();
81761 if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
81762 return continueTo(chooseCategoryBuilding);
81766 function continueTo(nextStep) {
81767 context.container().select('.inspector-wrap').on('wheel.intro', null);
81768 context.container().select('.preset-list-button').on('click.intro', null);
81769 context.on('enter.intro', null);
81774 function closeEditorHouse() {
81775 if (!_houseID || !context.hasEntity(_houseID)) {
81779 var ids = context.selectedIDs();
81781 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
81782 context.enter(modeSelect(context, [_houseID]));
81785 context.history().checkpoint('hasHouse');
81786 context.on('exit.intro', function () {
81787 continueTo(rightClickHouse);
81789 timeout(function () {
81790 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
81791 button: icon('#iD-icon-close', 'inline')
81795 function continueTo(nextStep) {
81796 context.on('exit.intro', null);
81801 function rightClickHouse() {
81802 if (!_houseID) return chapter.restart();
81803 context.enter(modeBrowse(context));
81804 context.history().reset('hasHouse');
81805 var zoom = context.map().zoom();
81811 context.map().centerZoomEase(house, zoom, 500);
81812 context.on('enter.intro', function (mode) {
81813 if (mode.id !== 'select') return;
81814 var ids = context.selectedIDs();
81815 if (ids.length !== 1 || ids[0] !== _houseID) return;
81816 timeout(function () {
81817 var node = selectMenuItem(context, 'orthogonalize').node();
81819 continueTo(clickSquare);
81820 }, 50); // after menu visible
81822 context.map().on('move.intro drawn.intro', function () {
81823 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
81824 revealHouse(house, rightclickString, {
81828 context.history().on('change.intro', function () {
81829 continueTo(rightClickHouse);
81832 function continueTo(nextStep) {
81833 context.on('enter.intro', null);
81834 context.map().on('move.intro drawn.intro', null);
81835 context.history().on('change.intro', null);
81840 function clickSquare() {
81841 if (!_houseID) return chapter.restart();
81842 var entity = context.hasEntity(_houseID);
81843 if (!entity) return continueTo(rightClickHouse);
81844 var node = selectMenuItem(context, 'orthogonalize').node();
81847 return continueTo(rightClickHouse);
81850 var wasChanged = false;
81851 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
81854 context.on('enter.intro', function (mode) {
81855 if (mode.id === 'browse') {
81856 continueTo(rightClickHouse);
81857 } else if (mode.id === 'move' || mode.id === 'rotate') {
81858 continueTo(retryClickSquare);
81861 context.map().on('move.intro', function () {
81862 var node = selectMenuItem(context, 'orthogonalize').node();
81864 if (!wasChanged && !node) {
81865 return continueTo(rightClickHouse);
81868 reveal('.edit-menu', helpHtml('intro.buildings.square_building'), {
81873 context.history().on('change.intro', function () {
81875 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
81877 timeout(function () {
81878 if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature', {
81881 continueTo(doneSquare);
81883 continueTo(retryClickSquare);
81885 }, 500); // after transitioned actions
81888 function continueTo(nextStep) {
81889 context.on('enter.intro', null);
81890 context.map().on('move.intro', null);
81891 context.history().on('change.intro', null);
81896 function retryClickSquare() {
81897 context.enter(modeBrowse(context));
81898 revealHouse(house, helpHtml('intro.buildings.retry_square'), {
81899 buttonText: _t.html('intro.ok'),
81900 buttonCallback: function buttonCallback() {
81901 continueTo(rightClickHouse);
81905 function continueTo(nextStep) {
81910 function doneSquare() {
81911 context.history().checkpoint('doneSquare');
81912 revealHouse(house, helpHtml('intro.buildings.done_square'), {
81913 buttonText: _t.html('intro.ok'),
81914 buttonCallback: function buttonCallback() {
81915 continueTo(addTank);
81919 function continueTo(nextStep) {
81924 function addTank() {
81925 context.enter(modeBrowse(context));
81926 context.history().reset('doneSquare');
81928 var msec = transitionTime(tank, context.map().center());
81931 reveal(null, null, {
81936 context.map().centerZoomEase(tank, 19.5, msec);
81937 timeout(function () {
81938 reveal('button.add-area', helpHtml('intro.buildings.add_tank'));
81939 context.on('enter.intro', function (mode) {
81940 if (mode.id !== 'add-area') return;
81941 continueTo(startTank);
81945 function continueTo(nextStep) {
81946 context.on('enter.intro', null);
81951 function startTank() {
81952 if (context.mode().id !== 'add-area') {
81953 return continueTo(addTank);
81957 timeout(function () {
81958 var startString = helpHtml('intro.buildings.start_tank') + helpHtml('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
81959 revealTank(tank, startString);
81960 context.map().on('move.intro drawn.intro', function () {
81961 revealTank(tank, startString, {
81965 context.on('enter.intro', function (mode) {
81966 if (mode.id !== 'draw-area') return chapter.restart();
81967 continueTo(continueTank);
81969 }, 550); // after easing
81971 function continueTo(nextStep) {
81972 context.map().on('move.intro drawn.intro', null);
81973 context.on('enter.intro', null);
81978 function continueTank() {
81979 if (context.mode().id !== 'draw-area') {
81980 return continueTo(addTank);
81984 var continueString = helpHtml('intro.buildings.continue_tank') + '{br}' + helpHtml('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) + helpHtml('intro.buildings.finish_tank');
81985 revealTank(tank, continueString);
81986 context.map().on('move.intro drawn.intro', function () {
81987 revealTank(tank, continueString, {
81991 context.on('enter.intro', function (mode) {
81992 if (mode.id === 'draw-area') {
81994 } else if (mode.id === 'select') {
81995 _tankID = context.selectedIDs()[0];
81996 return continueTo(searchPresetTank);
81998 return continueTo(addTank);
82002 function continueTo(nextStep) {
82003 context.map().on('move.intro drawn.intro', null);
82004 context.on('enter.intro', null);
82009 function searchPresetTank() {
82010 if (!_tankID || !context.hasEntity(_tankID)) {
82014 var ids = context.selectedIDs();
82016 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
82017 context.enter(modeSelect(context, [_tankID]));
82018 } // disallow scrolling
82021 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
82022 timeout(function () {
82023 // reset pane, in case user somehow happened to change it..
82024 context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
82025 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
82026 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
82027 preset: tankPreset.name()
82029 }, 400); // after preset list pane visible..
82031 context.on('enter.intro', function (mode) {
82032 if (!_tankID || !context.hasEntity(_tankID)) {
82033 return continueTo(addTank);
82036 var ids = context.selectedIDs();
82038 if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
82039 // keep the user's area selected..
82040 context.enter(modeSelect(context, [_tankID])); // reset pane, in case user somehow happened to change it..
82042 context.container().select('.inspector-wrap .panewrap').style('right', '-100%'); // disallow scrolling
82044 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
82045 context.container().select('.preset-search-input').on('keydown.intro', null).on('keyup.intro', checkPresetSearch);
82046 reveal('.preset-search-input', helpHtml('intro.buildings.search_tank', {
82047 preset: tankPreset.name()
82049 context.history().on('change.intro', null);
82053 function checkPresetSearch() {
82054 var first = context.container().select('.preset-list-item:first-child');
82056 if (first.classed('preset-man_made-storage_tank')) {
82057 reveal(first.select('.preset-list-button').node(), helpHtml('intro.buildings.choose_tank', {
82058 preset: tankPreset.name()
82062 context.container().select('.preset-search-input').on('keydown.intro', eventCancel, true).on('keyup.intro', null);
82063 context.history().on('change.intro', function () {
82064 continueTo(closeEditorTank);
82069 function continueTo(nextStep) {
82070 context.container().select('.inspector-wrap').on('wheel.intro', null);
82071 context.on('enter.intro', null);
82072 context.history().on('change.intro', null);
82073 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
82078 function closeEditorTank() {
82079 if (!_tankID || !context.hasEntity(_tankID)) {
82083 var ids = context.selectedIDs();
82085 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
82086 context.enter(modeSelect(context, [_tankID]));
82089 context.history().checkpoint('hasTank');
82090 context.on('exit.intro', function () {
82091 continueTo(rightClickTank);
82093 timeout(function () {
82094 reveal('.entity-editor-pane', helpHtml('intro.buildings.close', {
82095 button: icon('#iD-icon-close', 'inline')
82099 function continueTo(nextStep) {
82100 context.on('exit.intro', null);
82105 function rightClickTank() {
82106 if (!_tankID) return continueTo(addTank);
82107 context.enter(modeBrowse(context));
82108 context.history().reset('hasTank');
82109 context.map().centerEase(tank, 500);
82110 timeout(function () {
82111 context.on('enter.intro', function (mode) {
82112 if (mode.id !== 'select') return;
82113 var ids = context.selectedIDs();
82114 if (ids.length !== 1 || ids[0] !== _tankID) return;
82115 timeout(function () {
82116 var node = selectMenuItem(context, 'circularize').node();
82118 continueTo(clickCircle);
82119 }, 50); // after menu visible
82121 var rightclickString = helpHtml('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
82122 revealTank(tank, rightclickString);
82123 context.map().on('move.intro drawn.intro', function () {
82124 revealTank(tank, rightclickString, {
82128 context.history().on('change.intro', function () {
82129 continueTo(rightClickTank);
82133 function continueTo(nextStep) {
82134 context.on('enter.intro', null);
82135 context.map().on('move.intro drawn.intro', null);
82136 context.history().on('change.intro', null);
82141 function clickCircle() {
82142 if (!_tankID) return chapter.restart();
82143 var entity = context.hasEntity(_tankID);
82144 if (!entity) return continueTo(rightClickTank);
82145 var node = selectMenuItem(context, 'circularize').node();
82148 return continueTo(rightClickTank);
82151 var wasChanged = false;
82152 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
82155 context.on('enter.intro', function (mode) {
82156 if (mode.id === 'browse') {
82157 continueTo(rightClickTank);
82158 } else if (mode.id === 'move' || mode.id === 'rotate') {
82159 continueTo(retryClickCircle);
82162 context.map().on('move.intro', function () {
82163 var node = selectMenuItem(context, 'circularize').node();
82165 if (!wasChanged && !node) {
82166 return continueTo(rightClickTank);
82169 reveal('.edit-menu', helpHtml('intro.buildings.circle_tank'), {
82174 context.history().on('change.intro', function () {
82176 context.history().on('change.intro', null); // Something changed. Wait for transition to complete and check undo annotation.
82178 timeout(function () {
82179 if (context.history().undoAnnotation() === _t('operations.circularize.annotation.feature', {
82184 continueTo(retryClickCircle);
82186 }, 500); // after transitioned actions
82189 function continueTo(nextStep) {
82190 context.on('enter.intro', null);
82191 context.map().on('move.intro', null);
82192 context.history().on('change.intro', null);
82197 function retryClickCircle() {
82198 context.enter(modeBrowse(context));
82199 revealTank(tank, helpHtml('intro.buildings.retry_circle'), {
82200 buttonText: _t.html('intro.ok'),
82201 buttonCallback: function buttonCallback() {
82202 continueTo(rightClickTank);
82206 function continueTo(nextStep) {
82212 dispatch$1.call('done');
82213 reveal('.ideditor', helpHtml('intro.buildings.play', {
82214 next: _t('intro.startediting.title')
82216 tooltipBox: '.intro-nav-wrap .chapter-startEditing',
82217 buttonText: _t.html('intro.ok'),
82218 buttonCallback: function buttonCallback() {
82219 reveal('.ideditor');
82224 chapter.enter = function () {
82228 chapter.exit = function () {
82229 timeouts.forEach(window.clearTimeout);
82230 context.on('enter.intro exit.intro', null);
82231 context.map().on('move.intro drawn.intro', null);
82232 context.history().on('change.intro', null);
82233 context.container().select('.inspector-wrap').on('wheel.intro', null);
82234 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
82235 context.container().select('.more-fields .combobox-input').on('click.intro', null);
82238 chapter.restart = function () {
82243 return utilRebind(chapter, dispatch$1, 'on');
82246 function uiIntroStartEditing(context, reveal) {
82247 var dispatch$1 = dispatch('done', 'startEditing');
82248 var modalSelection = select(null);
82250 title: 'intro.startediting.title'
82253 function showHelp() {
82254 reveal('.map-control.help-control', helpHtml('intro.startediting.help'), {
82255 buttonText: _t.html('intro.ok'),
82256 buttonCallback: function buttonCallback() {
82262 function shortcuts() {
82263 reveal('.map-control.help-control', helpHtml('intro.startediting.shortcuts'), {
82264 buttonText: _t.html('intro.ok'),
82265 buttonCallback: function buttonCallback() {
82271 function showSave() {
82272 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82274 reveal('.top-toolbar button.save', helpHtml('intro.startediting.save'), {
82275 buttonText: _t.html('intro.ok'),
82276 buttonCallback: function buttonCallback() {
82282 function showStart() {
82283 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82285 modalSelection = uiModal(context.container());
82286 modalSelection.select('.modal').attr('class', 'modal-splash modal');
82287 modalSelection.selectAll('.close').remove();
82288 var startbutton = modalSelection.select('.content').attr('class', 'fillL').append('button').attr('class', 'modal-section huge-modal-button').on('click', function () {
82289 modalSelection.remove();
82291 startbutton.append('svg').attr('class', 'illustration').append('use').attr('xlink:href', '#iD-logo-walkthrough');
82292 startbutton.append('h2').html(_t.html('intro.startediting.start'));
82293 dispatch$1.call('startEditing');
82296 chapter.enter = function () {
82300 chapter.exit = function () {
82301 modalSelection.remove();
82302 context.container().selectAll('.shaded').remove(); // in case user opened keyboard shortcuts
82305 return utilRebind(chapter, dispatch$1, 'on');
82309 welcome: uiIntroWelcome,
82310 navigation: uiIntroNavigation,
82311 point: uiIntroPoint,
82314 building: uiIntroBuilding,
82315 startEditing: uiIntroStartEditing
82317 var chapterFlow = ['welcome', 'navigation', 'point', 'area', 'line', 'building', 'startEditing'];
82318 function uiIntro(context) {
82319 var INTRO_IMAGERY = 'EsriWorldImageryClarity';
82320 var _introGraph = {};
82324 function intro(selection) {
82325 _mainFileFetcher.get('intro_graph').then(function (dataIntroGraph) {
82326 // create entities for intro graph and localize names
82327 for (var id in dataIntroGraph) {
82328 if (!_introGraph[id]) {
82329 _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
82333 selection.call(startIntro);
82334 })["catch"](function () {
82339 function startIntro(selection) {
82340 context.enter(modeBrowse(context)); // Save current map state
82342 var osm = context.connection();
82343 var history = context.history().toJSON();
82344 var hash = window.location.hash;
82345 var center = context.map().center();
82346 var zoom = context.map().zoom();
82347 var background = context.background().baseLayerSource();
82348 var overlays = context.background().overlayLayerSources();
82349 var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
82350 var caches = osm && osm.caches();
82351 var baseEntities = context.history().graph().base().entities; // Show sidebar and disable the sidebar resizing button
82352 // (this needs to be before `context.inIntro(true)`)
82354 context.ui().sidebar.expand();
82355 context.container().selectAll('button.sidebar-toggle').classed('disabled', true); // Block saving
82357 context.inIntro(true); // Load semi-real data used in intro
82360 osm.toggle(false).reset();
82363 context.history().reset();
82364 context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
82365 context.history().checkpoint('initial'); // Setup imagery
82367 var imagery = context.background().findSource(INTRO_IMAGERY);
82370 context.background().baseLayerSource(imagery);
82372 context.background().bing();
82375 overlays.forEach(function (d) {
82376 return context.background().toggleOverlayLayer(d);
82377 }); // Setup data layers (only OSM)
82379 var layers = context.layers();
82380 layers.all().forEach(function (item) {
82381 // if the layer has the function `enabled`
82382 if (typeof item.layer.enabled === 'function') {
82383 item.layer.enabled(item.id === 'osm');
82386 context.container().selectAll('.main-map .layer-background').style('opacity', 1);
82387 var curtain = uiCurtain(context.container().node());
82388 selection.call(curtain); // Store that the user started the walkthrough..
82390 corePreferences('walkthrough_started', 'yes'); // Restore previous walkthrough progress..
82392 var storedProgress = corePreferences('walkthrough_progress') || '';
82393 var progress = storedProgress.split(';').filter(Boolean);
82394 var chapters = chapterFlow.map(function (chapter, i) {
82395 var s = chapterUi[chapter](context, curtain.reveal).on('done', function () {
82396 buttons.filter(function (d) {
82397 return d.title === s.title;
82398 }).classed('finished', true);
82400 if (i < chapterFlow.length - 1) {
82401 var next = chapterFlow[i + 1];
82402 context.container().select("button.chapter-".concat(next)).classed('next', true);
82403 } // Store walkthrough progress..
82406 progress.push(chapter);
82407 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
82411 chapters[chapters.length - 1].on('startEditing', function () {
82412 // Store walkthrough progress..
82413 progress.push('startEditing');
82414 corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';')); // Store if walkthrough is completed..
82416 var incomplete = utilArrayDifference(chapterFlow, progress);
82418 if (!incomplete.length) {
82419 corePreferences('walkthrough_completed', 'yes');
82424 context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
82425 context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
82428 osm.toggle(true).reset().caches(caches);
82431 context.history().reset().merge(Object.values(baseEntities));
82432 context.background().baseLayerSource(background);
82433 overlays.forEach(function (d) {
82434 return context.background().toggleOverlayLayer(d);
82438 context.history().fromJSON(history, false);
82441 context.map().centerZoom(center, zoom);
82442 window.location.replace(hash);
82443 context.inIntro(false);
82445 var navwrap = selection.append('div').attr('class', 'intro-nav-wrap fillD');
82446 navwrap.append('svg').attr('class', 'intro-nav-wrap-logo').append('use').attr('xlink:href', '#iD-logo-walkthrough');
82447 var buttonwrap = navwrap.append('div').attr('class', 'joined').selectAll('button.chapter');
82448 var buttons = buttonwrap.data(chapters).enter().append('button').attr('class', function (d, i) {
82449 return "chapter chapter-".concat(chapterFlow[i]);
82450 }).on('click', enterChapter);
82451 buttons.append('span').html(function (d) {
82452 return _t.html(d.title);
82454 buttons.append('span').attr('class', 'status').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
82455 enterChapter(null, chapters[0]);
82457 function enterChapter(d3_event, newChapter) {
82458 if (_currChapter) {
82459 _currChapter.exit();
82462 context.enter(modeBrowse(context));
82463 _currChapter = newChapter;
82465 _currChapter.enter();
82467 buttons.classed('next', false).classed('active', function (d) {
82468 return d.title === _currChapter.title;
82476 function uiIssuesInfo(context) {
82477 var warningsItem = {
82480 iconID: 'iD-icon-alert',
82481 descriptionID: 'issues.warnings_and_errors'
82483 var resolvedItem = {
82486 iconID: 'iD-icon-apply',
82487 descriptionID: 'issues.user_resolved_issues'
82490 function update(selection) {
82491 var shownItems = [];
82492 var liveIssues = context.validator().getIssues({
82493 what: corePreferences('validate-what') || 'edited',
82494 where: corePreferences('validate-where') || 'all'
82497 if (liveIssues.length) {
82498 warningsItem.count = liveIssues.length;
82499 shownItems.push(warningsItem);
82502 if (corePreferences('validate-what') === 'all') {
82503 var resolvedIssues = context.validator().getResolvedIssues();
82505 if (resolvedIssues.length) {
82506 resolvedItem.count = resolvedIssues.length;
82507 shownItems.push(resolvedItem);
82511 var chips = selection.selectAll('.chip').data(shownItems, function (d) {
82514 chips.exit().remove();
82515 var enter = chips.enter().append('a').attr('class', function (d) {
82516 return 'chip ' + d.id + '-count';
82517 }).attr('href', '#').each(function (d) {
82518 var chipSelection = select(this);
82519 var tooltipBehavior = uiTooltip().placement('top').title(_t.html(d.descriptionID));
82520 chipSelection.call(tooltipBehavior).on('click', function (d3_event) {
82521 d3_event.preventDefault();
82522 tooltipBehavior.hide(select(this)); // open the Issues pane
82524 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
82526 chipSelection.call(svgIcon('#' + d.iconID));
82528 enter.append('span').attr('class', 'count');
82529 enter.merge(chips).selectAll('span.count').html(function (d) {
82530 return d.count.toString();
82534 return function (selection) {
82536 context.validator().on('validated.infobox', function () {
82542 function uiMapInMap(context) {
82543 function mapInMap(selection) {
82544 var backgroundLayer = rendererTileLayer(context);
82545 var overlayLayers = {};
82546 var projection = geoRawMercator();
82547 var dataLayer = svgData(projection, context).showLabels(false);
82548 var debugLayer = svgDebug(projection, context);
82549 var zoom = d3_zoom().scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)]).on('start', zoomStarted).on('zoom', zoomed).on('end', zoomEnded);
82550 var wrap = select(null);
82551 var tiles = select(null);
82552 var viewport = select(null);
82553 var _isTransformed = false;
82554 var _isHidden = true;
82555 var _skipEvents = false;
82556 var _gesture = null;
82557 var _zDiff = 6; // by default, minimap renders at (main zoom - 6)
82559 var _dMini; // dimensions of minimap
82562 var _cMini; // center pixel of minimap
82565 var _tStart; // transform at start of gesture
82568 var _tCurr; // transform at most recent event
82573 function zoomStarted() {
82574 if (_skipEvents) return;
82575 _tStart = _tCurr = projection.transform();
82579 function zoomed(d3_event) {
82580 if (_skipEvents) return;
82581 var x = d3_event.transform.x;
82582 var y = d3_event.transform.y;
82583 var k = d3_event.transform.k;
82584 var isZooming = k !== _tStart.k;
82585 var isPanning = x !== _tStart.x || y !== _tStart.y;
82587 if (!isZooming && !isPanning) {
82588 return; // no change
82589 } // lock in either zooming or panning, don't allow both in minimap.
82593 _gesture = isZooming ? 'zoom' : 'pan';
82596 var tMini = projection.transform();
82599 if (_gesture === 'zoom') {
82600 scale = k / tMini.k;
82601 tX = (_cMini[0] / scale - _cMini[0]) * scale;
82602 tY = (_cMini[1] / scale - _cMini[1]) * scale;
82610 utilSetTransform(tiles, tX, tY, scale);
82611 utilSetTransform(viewport, 0, 0, scale);
82612 _isTransformed = true;
82613 _tCurr = identity$2.translate(x, y).scale(k);
82614 var zMain = geoScaleToZoom(context.projection.scale());
82615 var zMini = geoScaleToZoom(k);
82616 _zDiff = zMain - zMini;
82620 function zoomEnded() {
82621 if (_skipEvents) return;
82622 if (_gesture !== 'pan') return;
82623 updateProjection();
82625 context.map().center(projection.invert(_cMini)); // recenter main map..
82628 function updateProjection() {
82629 var loc = context.map().center();
82630 var tMain = context.projection.transform();
82631 var zMain = geoScaleToZoom(tMain.k);
82632 var zMini = Math.max(zMain - _zDiff, 0.5);
82633 var kMini = geoZoomToScale(zMini);
82634 projection.translate([tMain.x, tMain.y]).scale(kMini);
82635 var point = projection(loc);
82636 var mouse = _gesture === 'pan' ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
82637 var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
82638 var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
82639 projection.translate([xMini, yMini]).clipExtent([[0, 0], _dMini]);
82640 _tCurr = projection.transform();
82642 if (_isTransformed) {
82643 utilSetTransform(tiles, 0, 0);
82644 utilSetTransform(viewport, 0, 0);
82645 _isTransformed = false;
82648 zoom.scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
82649 _skipEvents = true;
82650 wrap.call(zoom.transform, _tCurr);
82651 _skipEvents = false;
82654 function redraw() {
82655 clearTimeout(_timeoutID);
82656 if (_isHidden) return;
82657 updateProjection();
82658 var zMini = geoScaleToZoom(projection.scale()); // setup tile container
82660 tiles = wrap.selectAll('.map-in-map-tiles').data([0]);
82661 tiles = tiles.enter().append('div').attr('class', 'map-in-map-tiles').merge(tiles); // redraw background
82663 backgroundLayer.source(context.background().baseLayerSource()).projection(projection).dimensions(_dMini);
82664 var background = tiles.selectAll('.map-in-map-background').data([0]);
82665 background.enter().append('div').attr('class', 'map-in-map-background').merge(background).call(backgroundLayer); // redraw overlay
82667 var overlaySources = context.background().overlayLayerSources();
82668 var activeOverlayLayers = [];
82670 for (var i = 0; i < overlaySources.length; i++) {
82671 if (overlaySources[i].validZoom(zMini)) {
82672 if (!overlayLayers[i]) overlayLayers[i] = rendererTileLayer(context);
82673 activeOverlayLayers.push(overlayLayers[i].source(overlaySources[i]).projection(projection).dimensions(_dMini));
82677 var overlay = tiles.selectAll('.map-in-map-overlay').data([0]);
82678 overlay = overlay.enter().append('div').attr('class', 'map-in-map-overlay').merge(overlay);
82679 var overlays = overlay.selectAll('div').data(activeOverlayLayers, function (d) {
82680 return d.source().name();
82682 overlays.exit().remove();
82683 overlays = overlays.enter().append('div').merge(overlays).each(function (layer) {
82684 select(this).call(layer);
82686 var dataLayers = tiles.selectAll('.map-in-map-data').data([0]);
82687 dataLayers.exit().remove();
82688 dataLayers = dataLayers.enter().append('svg').attr('class', 'map-in-map-data').merge(dataLayers).call(dataLayer).call(debugLayer); // redraw viewport bounding box
82690 if (_gesture !== 'pan') {
82691 var getPath = d3_geoPath(projection);
82694 coordinates: [context.map().extent().polygon()]
82696 viewport = wrap.selectAll('.map-in-map-viewport').data([0]);
82697 viewport = viewport.enter().append('svg').attr('class', 'map-in-map-viewport').merge(viewport);
82698 var path = viewport.selectAll('.map-in-map-bbox').data([bbox]);
82699 path.enter().append('path').attr('class', 'map-in-map-bbox').merge(path).attr('d', getPath).classed('thick', function (d) {
82700 return getPath.area(d) < 30;
82705 function queueRedraw() {
82706 clearTimeout(_timeoutID);
82707 _timeoutID = setTimeout(function () {
82712 function toggle(d3_event) {
82713 if (d3_event) d3_event.preventDefault();
82714 _isHidden = !_isHidden;
82715 context.container().select('.minimap-toggle-item').classed('active', !_isHidden).select('input').property('checked', !_isHidden);
82718 wrap.style('display', 'block').style('opacity', '1').transition().duration(200).style('opacity', '0').on('end', function () {
82719 selection.selectAll('.map-in-map').style('display', 'none');
82722 wrap.style('display', 'block').style('opacity', '0').transition().duration(200).style('opacity', '1').on('end', function () {
82728 uiMapInMap.toggle = toggle;
82729 wrap = selection.selectAll('.map-in-map').data([0]);
82730 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..
82732 _dMini = [200, 150]; //utilGetDimensions(wrap);
82734 _cMini = geoVecScale(_dMini, 0.5);
82735 context.map().on('drawn.map-in-map', function (drawn) {
82736 if (drawn.full === true) {
82741 context.keybinding().on(_t('background.minimap.key'), toggle);
82747 function uiNotice(context) {
82748 return function (selection) {
82749 var div = selection.append('div').attr('class', 'notice');
82750 var button = div.append('button').attr('class', 'zoom-to notice fillD').on('click', function () {
82751 context.map().zoomEase(context.minEditableZoom());
82752 }).on('wheel', function (d3_event) {
82753 // let wheel events pass through #4482
82754 var e2 = new WheelEvent(d3_event.type, d3_event);
82755 context.surface().node().dispatchEvent(e2);
82757 button.call(svgIcon('#iD-icon-plus', 'pre-text')).append('span').attr('class', 'label').html(_t.html('zoom_in_edit'));
82759 function disableTooHigh() {
82760 var canEdit = context.map().zoom() >= context.minEditableZoom();
82761 div.style('display', canEdit ? 'none' : 'block');
82764 context.map().on('move.notice', debounce(disableTooHigh, 500));
82769 function uiPhotoviewer(context) {
82770 var dispatch$1 = dispatch('resize');
82772 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
82774 function photoviewer(selection) {
82775 selection.append('button').attr('class', 'thumb-hide').on('click', function () {
82776 if (services.streetside) {
82777 services.streetside.hideViewer(context);
82780 if (services.mapillary) {
82781 services.mapillary.hideViewer(context);
82784 if (services.openstreetcam) {
82785 services.openstreetcam.hideViewer(context);
82787 }).append('div').call(svgIcon('#iD-icon-close'));
82789 function preventDefault(d3_event) {
82790 d3_event.preventDefault();
82793 selection.append('button').attr('class', 'resize-handle-xy').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82797 selection.append('button').attr('class', 'resize-handle-x').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82800 selection.append('button').attr('class', 'resize-handle-y').on('touchstart touchdown touchend', preventDefault).on(_pointerPrefix + 'down', buildResizeListener(selection, 'resize', dispatch$1, {
82804 function buildResizeListener(target, eventName, dispatch, options) {
82805 var resizeOnX = !!options.resizeOnX;
82806 var resizeOnY = !!options.resizeOnY;
82807 var minHeight = options.minHeight || 240;
82808 var minWidth = options.minWidth || 320;
82815 function startResize(d3_event) {
82816 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
82817 d3_event.preventDefault();
82818 d3_event.stopPropagation();
82819 var mapSize = context.map().dimensions();
82822 var maxWidth = mapSize[0];
82823 var newWidth = clamp(startWidth + d3_event.clientX - startX, minWidth, maxWidth);
82824 target.style('width', newWidth + 'px');
82828 var maxHeight = mapSize[1] - 90; // preserve space at top/bottom of map
82830 var newHeight = clamp(startHeight + startY - d3_event.clientY, minHeight, maxHeight);
82831 target.style('height', newHeight + 'px');
82834 dispatch.call(eventName, target, utilGetDimensions(target, true));
82837 function clamp(num, min, max) {
82838 return Math.max(min, Math.min(num, max));
82841 function stopResize(d3_event) {
82842 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
82843 d3_event.preventDefault();
82844 d3_event.stopPropagation(); // remove all the listeners we added
82846 select(window).on('.' + eventName, null);
82849 return function initResize(d3_event) {
82850 d3_event.preventDefault();
82851 d3_event.stopPropagation();
82852 pointerId = d3_event.pointerId || 'mouse';
82853 startX = d3_event.clientX;
82854 startY = d3_event.clientY;
82855 var targetRect = target.node().getBoundingClientRect();
82856 startWidth = targetRect.width;
82857 startHeight = targetRect.height;
82858 select(window).on(_pointerPrefix + 'move.' + eventName, startResize, false).on(_pointerPrefix + 'up.' + eventName, stopResize, false);
82860 if (_pointerPrefix === 'pointer') {
82861 select(window).on('pointercancel.' + eventName, stopResize, false);
82867 photoviewer.onMapResize = function () {
82868 var photoviewer = context.container().select('.photoviewer');
82869 var content = context.container().select('.main-content');
82870 var mapDimensions = utilGetDimensions(content, true); // shrink photo viewer if it is too big
82871 // (-90 preserves space at top and bottom of map used by menus)
82873 var photoDimensions = utilGetDimensions(photoviewer, true);
82875 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > mapDimensions[1] - 90) {
82876 var setPhotoDimensions = [Math.min(photoDimensions[0], mapDimensions[0]), Math.min(photoDimensions[1], mapDimensions[1] - 90)];
82877 photoviewer.style('width', setPhotoDimensions[0] + 'px').style('height', setPhotoDimensions[1] + 'px');
82878 dispatch$1.call('resize', photoviewer, setPhotoDimensions);
82882 return utilRebind(photoviewer, dispatch$1, 'on');
82885 function uiRestore(context) {
82886 return function (selection) {
82887 if (!context.history().hasRestorableChanges()) return;
82888 var modalSelection = uiModal(selection, true);
82889 modalSelection.select('.modal').attr('class', 'modal fillL');
82890 var introModal = modalSelection.select('.content');
82891 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('restore.heading'));
82892 introModal.append('div').attr('class', 'modal-section').append('p').html(_t.html('restore.description'));
82893 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
82894 var restore = buttonWrap.append('button').attr('class', 'restore').on('click', function () {
82895 context.history().restore();
82896 modalSelection.remove();
82898 restore.append('svg').attr('class', 'logo logo-restore').append('use').attr('xlink:href', '#iD-logo-restore');
82899 restore.append('div').html(_t.html('restore.restore'));
82900 var reset = buttonWrap.append('button').attr('class', 'reset').on('click', function () {
82901 context.history().clearSaved();
82902 modalSelection.remove();
82904 reset.append('svg').attr('class', 'logo logo-reset').append('use').attr('xlink:href', '#iD-logo-reset');
82905 reset.append('div').html(_t.html('restore.reset'));
82906 restore.node().focus();
82910 function uiScale(context) {
82911 var projection = context.projection,
82912 isImperial = !_mainLocalizer.usesMetric(),
82916 function scaleDefs(loc1, loc2) {
82917 var lat = (loc2[1] + loc1[1]) / 2,
82918 conversion = isImperial ? 3.28084 : 1,
82919 dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
82931 buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
82933 buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
82934 } // determine a user-friendly endpoint for the scale
82937 for (i = 0; i < buckets.length; i++) {
82941 scale.dist = Math.floor(dist / val) * val;
82944 scale.dist = +dist.toFixed(2);
82948 dLon = geoMetersToLon(scale.dist / conversion, lat);
82949 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
82950 scale.text = displayLength(scale.dist / conversion, isImperial);
82954 function update(selection) {
82955 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
82956 var dims = context.map().dimensions(),
82957 loc1 = projection.invert([0, dims[1]]),
82958 loc2 = projection.invert([maxLength, dims[1]]),
82959 scale = scaleDefs(loc1, loc2);
82960 selection.select('.scale-path').attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
82961 selection.select('.scale-text').style(_mainLocalizer.textDirection() === 'ltr' ? 'left' : 'right', scale.px + 16 + 'px').html(scale.text);
82964 return function (selection) {
82965 function switchUnits() {
82966 isImperial = !isImperial;
82967 selection.call(update);
82970 var scalegroup = selection.append('svg').attr('class', 'scale').on('click', switchUnits).append('g').attr('transform', 'translate(10,11)');
82971 scalegroup.append('path').attr('class', 'scale-path');
82972 selection.append('div').attr('class', 'scale-text');
82973 selection.call(update);
82974 context.map().on('move.scale', function () {
82980 function uiShortcuts(context) {
82981 var detected = utilDetect();
82982 var _activeTab = 0;
82984 var _modalSelection;
82986 var _selection = select(null);
82988 var _dataShortcuts;
82990 function shortcutsModal(_modalSelection) {
82991 _modalSelection.select('.modal').classed('modal-shortcuts', true);
82993 var content = _modalSelection.select('.content');
82995 content.append('div').attr('class', 'modal-section').append('h3').html(_t.html('shortcuts.title'));
82996 _mainFileFetcher.get('shortcuts').then(function (data) {
82997 _dataShortcuts = data;
82998 content.call(render);
82999 })["catch"](function () {
83004 function render(selection) {
83005 if (!_dataShortcuts) return;
83006 var wrapper = selection.selectAll('.wrapper').data([0]);
83007 var wrapperEnter = wrapper.enter().append('div').attr('class', 'wrapper modal-section');
83008 var tabsBar = wrapperEnter.append('div').attr('class', 'tabs-bar');
83009 var shortcutsList = wrapperEnter.append('div').attr('class', 'shortcuts-list');
83010 wrapper = wrapper.merge(wrapperEnter);
83011 var tabs = tabsBar.selectAll('.tab').data(_dataShortcuts);
83012 var tabsEnter = tabs.enter().append('a').attr('class', 'tab').attr('href', '#').on('click', function (d3_event, d) {
83013 d3_event.preventDefault();
83015 var i = _dataShortcuts.indexOf(d);
83020 tabsEnter.append('span').html(function (d) {
83021 return _t.html(d.text);
83024 wrapper.selectAll('.tab').classed('active', function (d, i) {
83025 return i === _activeTab;
83027 var shortcuts = shortcutsList.selectAll('.shortcut-tab').data(_dataShortcuts);
83028 var shortcutsEnter = shortcuts.enter().append('div').attr('class', function (d) {
83029 return 'shortcut-tab shortcut-tab-' + d.tab;
83031 var columnsEnter = shortcutsEnter.selectAll('.shortcut-column').data(function (d) {
83033 }).enter().append('table').attr('class', 'shortcut-column');
83034 var rowsEnter = columnsEnter.selectAll('.shortcut-row').data(function (d) {
83036 }).enter().append('tr').attr('class', 'shortcut-row');
83037 var sectionRows = rowsEnter.filter(function (d) {
83038 return !d.shortcuts;
83040 sectionRows.append('td');
83041 sectionRows.append('td').attr('class', 'shortcut-section').append('h3').html(function (d) {
83042 return _t.html(d.text);
83044 var shortcutRows = rowsEnter.filter(function (d) {
83045 return d.shortcuts;
83047 var shortcutKeys = shortcutRows.append('td').attr('class', 'shortcut-keys');
83048 var modifierKeys = shortcutKeys.filter(function (d) {
83049 return d.modifiers;
83051 modifierKeys.selectAll('kbd.modifier').data(function (d) {
83052 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
83054 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
83057 return d.modifiers;
83059 }).enter().each(function () {
83060 var selection = select(this);
83061 selection.append('kbd').attr('class', 'modifier').html(function (d) {
83062 return uiCmd.display(d);
83064 selection.append('span').html('+');
83066 shortcutKeys.selectAll('kbd.shortcut').data(function (d) {
83067 var arr = d.shortcuts;
83069 if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
83071 } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
83073 } // replace translations
83076 arr = arr.map(function (s) {
83077 return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
83079 return utilArrayUniq(arr).map(function (s) {
83082 separator: d.separator,
83086 }).enter().each(function (d, i, nodes) {
83087 var selection = select(this);
83088 var click = d.shortcut.toLowerCase().match(/(.*).click/);
83090 if (click && click[1]) {
83091 // replace "left_click", "right_click" with mouse icon
83092 selection.call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
83093 } else if (d.shortcut.toLowerCase() === 'long-press') {
83094 selection.call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
83095 } else if (d.shortcut.toLowerCase() === 'tap') {
83096 selection.call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
83098 selection.append('kbd').attr('class', 'shortcut').html(function (d) {
83103 if (i < nodes.length - 1) {
83104 selection.append('span').html(d.separator || "\xA0" + _t.html('shortcuts.or') + "\xA0");
83105 } else if (i === nodes.length - 1 && d.suffix) {
83106 selection.append('span').html(d.suffix);
83109 shortcutKeys.filter(function (d) {
83111 }).each(function () {
83112 var selection = select(this);
83113 selection.append('span').html('+');
83114 selection.append('span').attr('class', 'gesture').html(function (d) {
83115 return _t.html(d.gesture);
83118 shortcutRows.append('td').attr('class', 'shortcut-desc').html(function (d) {
83119 return d.text ? _t.html(d.text) : "\xA0";
83122 wrapper.selectAll('.shortcut-tab').style('display', function (d, i) {
83123 return i === _activeTab ? 'flex' : 'none';
83127 return function (selection, show) {
83128 _selection = selection;
83131 _modalSelection = uiModal(selection);
83133 _modalSelection.call(shortcutsModal);
83135 context.keybinding().on([_t('shortcuts.toggle.key'), '?'], function () {
83136 if (context.container().selectAll('.modal-shortcuts').size()) {
83138 if (_modalSelection) {
83139 _modalSelection.close();
83141 _modalSelection = null;
83144 _modalSelection = uiModal(_selection);
83146 _modalSelection.call(shortcutsModal);
83155 function search(input, dims) {
83156 if (!dims) dims = 'NSEW';
83157 if (typeof input !== 'string') return null;
83158 input = input.toUpperCase();
83159 var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
83160 var m = input.match(regex);
83161 if (!m) return null; // no match
83163 var matched = m[0]; // extract dimension.. m[1] = leading, m[5] = trailing
83167 if (m[1] && m[5]) {
83168 // if matched both..
83169 dim = m[1]; // keep leading
83171 matched = matched.slice(0, -1); // remove trailing dimension from match
83173 dim = m[1] || m[5];
83174 } // if unrecognized dimension
83177 if (dim && dims.indexOf(dim) === -1) return null; // extract DMS
83179 var deg = m[2] ? parseFloat(m[2]) : 0;
83180 var min = m[3] ? parseFloat(m[3]) / 60 : 0;
83181 var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
83182 var sign = deg < 0 ? -1 : 1;
83183 if (dim === 'S' || dim === 'W') sign *= -1;
83185 val: (Math.abs(deg) + min + sec) * sign,
83188 remain: input.slice(matched.length)
83192 function pair(input, dims) {
83193 input = input.trim();
83194 var one = search(input, dims);
83195 if (!one) return null;
83196 input = one.remain.trim();
83197 var two = search(input, dims);
83198 if (!two || two.remain) return null;
83201 return swapdim(one.val, two.val, one.dim);
83203 return [one.val, two.val];
83207 function swapdim(a, b, dim) {
83208 if (dim === 'N' || dim === 'S') return [a, b];
83209 if (dim === 'W' || dim === 'E') return [b, a];
83212 function uiFeatureList(context) {
83213 var _geocodeResults;
83215 function featureList(selection) {
83216 var header = selection.append('div').attr('class', 'header fillL');
83217 header.append('h3').html(_t.html('inspector.feature_list'));
83218 var searchWrap = selection.append('div').attr('class', 'search-header');
83219 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
83220 var search = searchWrap.append('input').attr('placeholder', _t('inspector.search')).attr('type', 'search').call(utilNoAuto).on('keypress', keypress).on('keydown', keydown).on('input', inputevent);
83221 var listWrap = selection.append('div').attr('class', 'inspector-body');
83222 var list = listWrap.append('div').attr('class', 'feature-list');
83223 context.on('exit.feature-list', clearSearch);
83224 context.map().on('drawn.feature-list', mapDrawn);
83225 context.keybinding().on(uiCmd('⌘F'), focusSearch);
83227 function focusSearch(d3_event) {
83228 var mode = context.mode() && context.mode().id;
83229 if (mode !== 'browse') return;
83230 d3_event.preventDefault();
83231 search.node().focus();
83234 function keydown(d3_event) {
83235 if (d3_event.keyCode === 27) {
83237 search.node().blur();
83241 function keypress(d3_event) {
83242 var q = search.property('value'),
83243 items = list.selectAll('.feature-list-item');
83245 if (d3_event.keyCode === 13 && // ↩ Return
83246 q.length && items.size()) {
83247 click(items.datum());
83251 function inputevent() {
83252 _geocodeResults = undefined;
83256 function clearSearch() {
83257 search.property('value', '');
83261 function mapDrawn(e) {
83267 function features() {
83269 var graph = context.graph();
83270 var visibleCenter = context.map().extent().center();
83271 var q = search.property('value').toLowerCase();
83272 if (!q) return result;
83273 var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
83275 if (locationMatch) {
83276 var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
83280 type: _t('inspector.location'),
83281 name: dmsCoordinatePair([loc[1], loc[0]]),
83284 } // A location search takes priority over an ID search
83287 var idMatch = !locationMatch && q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
83290 var elemType = idMatch[1].charAt(0);
83291 var elemId = idMatch[2];
83293 id: elemType + elemId,
83294 geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
83295 type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
83300 var allEntities = graph.entities;
83301 var localResults = [];
83303 for (var id in allEntities) {
83304 var entity = allEntities[id];
83305 if (!entity) continue;
83306 var name = utilDisplayName(entity) || '';
83307 if (name.toLowerCase().indexOf(q) < 0) continue;
83308 var matched = _mainPresetIndex.match(entity, graph);
83309 var type = matched && matched.name() || utilDisplayType(entity.id);
83310 var extent = entity.extent(graph);
83311 var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
83312 localResults.push({
83315 geometry: entity.geometry(graph),
83320 if (localResults.length > 100) break;
83323 localResults = localResults.sort(function byDistance(a, b) {
83324 return a.distance - b.distance;
83326 result = result.concat(localResults);
83328 (_geocodeResults || []).forEach(function (d) {
83329 if (d.osm_type && d.osm_id) {
83330 // some results may be missing these - #1890
83331 // Make a temporary osmEntity so we can preset match
83332 // and better localize the search result - #4725
83333 var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
83335 tags[d["class"]] = d.type;
83342 if (d.osm_type === 'way') {
83343 // for ways, add some fake closed nodes
83344 attrs.nodes = ['a', 'a']; // so that geometry area is possible
83347 var tempEntity = osmEntity(attrs);
83348 var tempGraph = coreGraph([tempEntity]);
83349 var matched = _mainPresetIndex.match(tempEntity, tempGraph);
83350 var type = matched && matched.name() || utilDisplayType(id);
83353 geometry: tempEntity.geometry(tempGraph),
83355 name: d.display_name,
83356 extent: new geoExtent([parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])], [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
83361 if (q.match(/^[0-9]+$/)) {
83362 // if query is just a number, possibly an OSM ID without a prefix
83366 type: _t('inspector.node'),
83372 type: _t('inspector.way'),
83377 geometry: 'relation',
83378 type: _t('inspector.relation'),
83386 function drawList() {
83387 var value = search.property('value');
83388 var results = features();
83389 list.classed('filtered', value.length);
83390 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'));
83391 resultsIndicator.append('span').attr('class', 'entity-name');
83392 list.selectAll('.no-results-item .entity-name').html(_t.html('geocoder.no_results_worldwide'));
83394 if (services.geocoder) {
83395 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'));
83398 list.selectAll('.no-results-item').style('display', value.length && !results.length ? 'block' : 'none');
83399 list.selectAll('.geocode-item').style('display', value && _geocodeResults === undefined ? 'block' : 'none');
83400 list.selectAll('.feature-list-item').data([-1]).remove();
83401 var items = list.selectAll('.feature-list-item').data(results, function (d) {
83404 var enter = items.enter().insert('button', '.geocode-item').attr('class', 'feature-list-item').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
83405 var label = enter.append('div').attr('class', 'label');
83406 label.each(function (d) {
83407 select(this).call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
83409 label.append('span').attr('class', 'entity-type').html(function (d) {
83412 label.append('span').attr('class', 'entity-name').html(function (d) {
83415 enter.style('opacity', 0).transition().style('opacity', 1);
83417 items.exit().remove();
83420 function mouseover(d3_event, d) {
83421 if (d.id === -1) return;
83422 utilHighlightEntities([d.id], true, context);
83425 function mouseout(d3_event, d) {
83426 if (d.id === -1) return;
83427 utilHighlightEntities([d.id], false, context);
83430 function click(d3_event, d) {
83431 d3_event.preventDefault();
83434 context.map().centerZoomEase([d.location[1], d.location[0]], 19);
83435 } else if (d.entity) {
83436 utilHighlightEntities([d.id], false, context);
83437 context.enter(modeSelect(context, [d.entity.id]));
83438 context.map().zoomToEase(d.entity);
83440 // download, zoom to, and select the entity with the given ID
83441 context.zoomToEntity(d.id);
83445 function geocoderSearch() {
83446 services.geocoder.search(search.property('value'), function (err, resp) {
83447 _geocodeResults = resp || [];
83453 return featureList;
83456 function uiSectionEntityIssues(context) {
83457 var _entityIDs = [];
83460 var _activeIssueID;
83462 var section = uiSection('entity-issues', context).shouldDisplay(function () {
83463 return _issues.length > 0;
83464 }).label(function () {
83465 return _t('inspector.title_count', {
83466 title: _t.html('issues.list_title'),
83467 count: _issues.length
83469 }).disclosureContent(renderDisclosureContent);
83470 context.validator().on('validated.entity_issues', function () {
83471 // Refresh on validated events
83473 section.reRender();
83474 }).on('focusedIssue.entity_issues', function (issue) {
83475 makeActiveIssue(issue.id);
83478 function reloadIssues() {
83479 _issues = context.validator().getSharedEntityIssues(_entityIDs, {
83480 includeDisabledRules: true
83484 function makeActiveIssue(issueID) {
83485 _activeIssueID = issueID;
83486 section.selection().selectAll('.issue-container').classed('active', function (d) {
83487 return d.id === _activeIssueID;
83491 function renderDisclosureContent(selection) {
83492 selection.classed('grouped-items-area', true);
83493 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
83494 var containers = selection.selectAll('.issue-container').data(_issues, function (d) {
83498 containers.exit().remove(); // Enter
83500 var containersEnter = containers.enter().append('div').attr('class', 'issue-container');
83501 var itemsEnter = containersEnter.append('div').attr('class', function (d) {
83502 return 'issue severity-' + d.severity;
83503 }).on('mouseover.highlight', function (d3_event, d) {
83504 // don't hover-highlight the selected entity
83505 var ids = d.entityIds.filter(function (e) {
83506 return _entityIDs.indexOf(e) === -1;
83508 utilHighlightEntities(ids, true, context);
83509 }).on('mouseout.highlight', function (d3_event, d) {
83510 var ids = d.entityIds.filter(function (e) {
83511 return _entityIDs.indexOf(e) === -1;
83513 utilHighlightEntities(ids, false, context);
83515 var labelsEnter = itemsEnter.append('div').attr('class', 'issue-label');
83516 var textEnter = labelsEnter.append('button').attr('class', 'issue-text').on('click', function (d3_event, d) {
83517 makeActiveIssue(d.id); // expand only the clicked item
83519 var extent = d.extent(context.graph());
83522 var setZoom = Math.max(context.map().zoom(), 19);
83523 context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
83526 textEnter.each(function (d) {
83527 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
83528 select(this).call(svgIcon(iconName, 'issue-icon'));
83530 textEnter.append('span').attr('class', 'issue-message');
83531 var infoButton = labelsEnter.append('button').attr('class', 'issue-info-button').attr('title', _t('icons.information')).call(svgIcon('#iD-icon-inspect'));
83532 infoButton.on('click', function (d3_event) {
83533 d3_event.stopPropagation();
83534 d3_event.preventDefault();
83535 this.blur(); // avoid keeping focus on the button - #4641
83537 var container = select(this.parentNode.parentNode.parentNode);
83538 var info = container.selectAll('.issue-info');
83539 var isExpanded = info.classed('expanded');
83542 info.transition().duration(200).style('max-height', '0px').style('opacity', '0').on('end', function () {
83543 info.classed('expanded', false);
83546 info.classed('expanded', true).transition().duration(200).style('max-height', '200px').style('opacity', '1').on('end', function () {
83547 info.style('max-height', null);
83551 itemsEnter.append('ul').attr('class', 'issue-fix-list');
83552 containersEnter.append('div').attr('class', 'issue-info').style('max-height', '0').style('opacity', '0').each(function (d) {
83553 if (typeof d.reference === 'function') {
83554 select(this).call(d.reference);
83556 select(this).html(_t.html('inspector.no_documentation_key'));
83560 containers = containers.merge(containersEnter).classed('active', function (d) {
83561 return d.id === _activeIssueID;
83563 containers.selectAll('.issue-message').html(function (d) {
83564 return d.message(context);
83567 var fixLists = containers.selectAll('.issue-fix-list');
83568 var fixes = fixLists.selectAll('.issue-fix-item').data(function (d) {
83569 return d.fixes ? d.fixes(context) : [];
83570 }, function (fix) {
83573 fixes.exit().remove();
83574 var fixesEnter = fixes.enter().append('li').attr('class', 'issue-fix-item');
83575 var buttons = fixesEnter.append('button').on('click', function (d3_event, d) {
83576 // not all fixes are actionable
83577 if (select(this).attr('disabled') || !d.onClick) return; // Don't run another fix for this issue within a second of running one
83578 // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
83580 if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) return;
83581 d.issue.dateLastRanFix = new Date(); // remove hover-highlighting
83583 utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
83584 new Promise(function (resolve, reject) {
83585 d.onClick(context, resolve, reject);
83587 if (d.onClick.length <= 1) {
83588 // if the fix doesn't take any completion parameters then consider it resolved
83591 }).then(function () {
83592 // revalidate whenever the fix has finished running successfully
83593 context.validator().validate();
83595 }).on('mouseover.highlight', function (d3_event, d) {
83596 utilHighlightEntities(d.entityIds, true, context);
83597 }).on('mouseout.highlight', function (d3_event, d) {
83598 utilHighlightEntities(d.entityIds, false, context);
83600 buttons.each(function (d) {
83601 var iconName = d.icon || 'iD-icon-wrench';
83603 if (iconName.startsWith('maki')) {
83607 select(this).call(svgIcon('#' + iconName, 'fix-icon'));
83609 buttons.append('span').attr('class', 'fix-message').html(function (d) {
83612 fixesEnter.merge(fixes).selectAll('button').classed('actionable', function (d) {
83614 }).attr('disabled', function (d) {
83615 return d.onClick ? null : 'true';
83616 }).attr('title', function (d) {
83617 if (d.disabledReason) {
83618 return d.disabledReason;
83625 section.entityIDs = function (val) {
83626 if (!arguments.length) return _entityIDs;
83628 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
83630 _activeIssueID = null;
83640 function uiPresetIcon() {
83645 var _sizeClass = 'medium';
83647 function isSmall() {
83648 return _sizeClass === 'small';
83651 function presetIcon(selection) {
83652 selection.each(render);
83655 function getIcon(p, geom) {
83656 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';
83659 function renderPointBorder(container, drawPoint) {
83660 var pointBorder = container.selectAll('.preset-icon-point-border').data(drawPoint ? [0] : []);
83661 pointBorder.exit().remove();
83662 var pointBorderEnter = pointBorder.enter();
83665 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');
83666 pointBorder = pointBorderEnter.merge(pointBorder);
83669 function renderCircleFill(container, drawVertex) {
83670 var vertexFill = container.selectAll('.preset-icon-fill-vertex').data(drawVertex ? [0] : []);
83671 vertexFill.exit().remove();
83672 var vertexFillEnter = vertexFill.enter();
83676 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);
83677 vertexFill = vertexFillEnter.merge(vertexFill);
83680 function renderSquareFill(container, drawArea, tagClasses) {
83681 var fill = container.selectAll('.preset-icon-fill-area').data(drawArea ? [0] : []);
83682 fill.exit().remove();
83683 var fillEnter = fill.enter();
83684 var d = isSmall() ? 40 : 60;
83688 var c1 = (w - l) / 2;
83690 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));
83691 ['fill', 'stroke'].forEach(function (klass) {
83692 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));
83695 [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
83696 fillEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', rVertex);
83700 var rMidpoint = 1.25;
83701 [[c1, w / 2], [c2, w / 2], [h / 2, c1], [h / 2, c2]].forEach(function (point) {
83702 fillEnter.append('circle').attr('class', 'midpoint').attr('cx', point[0]).attr('cy', point[1]).attr('r', rMidpoint);
83706 fill = fillEnter.merge(fill);
83707 fill.selectAll('path.stroke').attr('class', "area stroke ".concat(tagClasses));
83708 fill.selectAll('path.fill').attr('class', "area fill ".concat(tagClasses));
83711 function renderLine(container, drawLine, tagClasses) {
83712 var line = container.selectAll('.preset-icon-line').data(drawLine ? [0] : []);
83713 line.exit().remove();
83714 var lineEnter = line.enter();
83715 var d = isSmall() ? 40 : 60; // draw the line parametrically
83719 var y = Math.round(d * 0.72);
83720 var l = Math.round(d * 0.6);
83722 var x1 = (w - l) / 2;
83724 lineEnter = lineEnter.append('svg').attr('class', 'preset-icon-line').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
83725 ['casing', 'stroke'].forEach(function (klass) {
83726 lineEnter.append('path').attr('d', "M".concat(x1, " ").concat(y, " L").concat(x2, " ").concat(y)).attr('class', "line ".concat(klass));
83728 [[x1 - 1, y], [x2 + 1, y]].forEach(function (point) {
83729 lineEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
83731 line = lineEnter.merge(line);
83732 line.selectAll('path.stroke').attr('class', "line stroke ".concat(tagClasses));
83733 line.selectAll('path.casing').attr('class', "line casing ".concat(tagClasses));
83736 function renderRoute(container, drawRoute, p) {
83737 var route = container.selectAll('.preset-icon-route').data(drawRoute ? [0] : []);
83738 route.exit().remove();
83739 var routeEnter = route.enter();
83740 var d = isSmall() ? 40 : 60; // draw the route parametrically
83744 var y1 = Math.round(d * 0.80);
83745 var y2 = Math.round(d * 0.68);
83746 var l = Math.round(d * 0.6);
83748 var x1 = (w - l) / 2;
83749 var x2 = x1 + l / 3;
83750 var x3 = x2 + l / 3;
83751 var x4 = x3 + l / 3;
83752 routeEnter = routeEnter.append('svg').attr('class', 'preset-icon-route').attr('width', w).attr('height', h).attr('viewBox', "0 0 ".concat(w, " ").concat(h));
83753 ['casing', 'stroke'].forEach(function (klass) {
83754 routeEnter.append('path').attr('d', "M".concat(x1, " ").concat(y1, " L").concat(x2, " ").concat(y2)).attr('class', "segment0 line ".concat(klass));
83755 routeEnter.append('path').attr('d', "M".concat(x2, " ").concat(y2, " L").concat(x3, " ").concat(y1)).attr('class', "segment1 line ".concat(klass));
83756 routeEnter.append('path').attr('d', "M".concat(x3, " ").concat(y1, " L").concat(x4, " ").concat(y2)).attr('class', "segment2 line ".concat(klass));
83758 [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
83759 routeEnter.append('circle').attr('class', 'vertex').attr('cx', point[0]).attr('cy', point[1]).attr('r', r);
83761 route = routeEnter.merge(route);
83764 var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
83765 var segmentPresetIDs = routeSegments[routeType];
83767 for (var i in segmentPresetIDs) {
83768 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
83769 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
83770 route.selectAll("path.stroke.segment".concat(i)).attr('class', "segment".concat(i, " line stroke ").concat(segmentTagClasses));
83771 route.selectAll("path.casing.segment".concat(i)).attr('class', "segment".concat(i, " line casing ").concat(segmentTagClasses));
83774 } // Route icons are drawn with a zigzag annotation underneath:
83778 // This dataset defines the styles that are used to draw the zigzag segments.
83781 var routeSegments = {
83782 bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
83783 bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
83784 trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
83785 detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
83786 ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
83787 foot: ['highway/footway', 'highway/footway', 'highway/footway'],
83788 hiking: ['highway/path', 'highway/path', 'highway/path'],
83789 horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
83790 light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
83791 monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
83792 pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
83793 piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
83794 power: ['power/line', 'power/line', 'power/line'],
83795 road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
83796 subway: ['railway/subway', 'railway/subway', 'railway/subway'],
83797 train: ['railway/rail', 'railway/rail', 'railway/rail'],
83798 tram: ['railway/tram', 'railway/tram', 'railway/tram'],
83799 waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
83802 function render() {
83803 var p = _preset.apply(this, arguments);
83805 var geom = _geometry ? _geometry.apply(this, arguments) : null;
83807 if (geom === 'relation' && p.tags && (p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route] || p.tags.type === 'waterway')) {
83811 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
83812 var isFallback = isSmall() && p.isFallback && p.isFallback();
83813 var imageURL = showThirdPartyIcons === 'true' && p.imageURL;
83814 var picon = getIcon(p, geom);
83815 var isMaki = picon && /^maki-/.test(picon);
83816 var isTemaki = picon && /^temaki-/.test(picon);
83817 var isFa = picon && /^fa[srb]-/.test(picon);
83818 var isiDIcon = picon && !(isMaki || isTemaki || isFa);
83819 var isCategory = !p.setTags;
83820 var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
83821 var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
83822 var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
83823 var drawArea = picon && geom === 'area' && !isFallback;
83824 var drawRoute = picon && geom === 'route';
83825 var isFramed = drawVertex || drawArea || drawLine || drawRoute;
83826 var tags = !isCategory ? p.setTags({}, geom) : {};
83828 for (var k in tags) {
83829 if (tags[k] === '*') {
83834 var tagClasses = svgTagClasses().getClassesString(tags, '');
83835 var selection = select(this);
83836 var container = selection.selectAll('.preset-icon-container').data([0]);
83837 container = container.enter().append('div').attr('class', "preset-icon-container ".concat(_sizeClass)).merge(container);
83838 container.classed('showing-img', !!imageURL).classed('fallback', isFallback);
83839 renderPointBorder(container, drawPoint);
83840 renderCircleFill(container, drawVertex);
83841 renderSquareFill(container, drawArea, tagClasses);
83842 renderLine(container, drawLine, tagClasses);
83843 renderRoute(container, drawRoute, p);
83844 var icon = container.selectAll('.preset-icon').data(picon ? [0] : []);
83845 icon.exit().remove();
83846 icon = icon.enter().append('div').attr('class', 'preset-icon').call(svgIcon('')).merge(icon);
83847 icon.attr('class', 'preset-icon ' + (geom ? geom + '-geom' : '')).classed('framed', isFramed).classed('preset-icon-iD', isiDIcon);
83848 icon.selectAll('svg').attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line' ? '' : tagClasses));
83849 icon.selectAll('use').attr('href', '#' + picon + (isMaki ? isSmall() && geom === 'point' ? '-11' : '-15' : ''));
83850 var imageIcon = container.selectAll('img.image-icon').data(imageURL ? [0] : []);
83851 imageIcon.exit().remove();
83852 imageIcon = imageIcon.enter().append('img').attr('class', 'image-icon').on('load', function () {
83853 return container.classed('showing-img', true);
83854 }).on('error', function () {
83855 return container.classed('showing-img', false);
83856 }).merge(imageIcon);
83857 imageIcon.attr('src', imageURL);
83860 presetIcon.preset = function (val) {
83861 if (!arguments.length) return _preset;
83862 _preset = utilFunctor(val);
83866 presetIcon.geometry = function (val) {
83867 if (!arguments.length) return _geometry;
83868 _geometry = utilFunctor(val);
83872 presetIcon.sizeClass = function (val) {
83873 if (!arguments.length) return _sizeClass;
83881 function uiSectionFeatureType(context) {
83882 var dispatch$1 = dispatch('choose');
83883 var _entityIDs = [];
83888 var section = uiSection('feature-type', context).label(_t.html('inspector.feature_type')).disclosureContent(renderDisclosureContent);
83890 function renderDisclosureContent(selection) {
83891 selection.classed('preset-list-item', true);
83892 selection.classed('mixed-types', _presets.length > 1);
83893 var presetButtonWrap = selection.selectAll('.preset-list-button-wrap').data([0]).enter().append('div').attr('class', 'preset-list-button-wrap');
83894 var presetButton = presetButtonWrap.append('button').attr('class', 'preset-list-button preset-reset').call(uiTooltip().title(_t.html('inspector.back_tooltip')).placement('bottom'));
83895 presetButton.append('div').attr('class', 'preset-icon-container');
83896 presetButton.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
83897 presetButtonWrap.append('div').attr('class', 'accessory-buttons');
83898 var tagReferenceBodyWrap = selection.selectAll('.tag-reference-body-wrap').data([0]);
83899 tagReferenceBodyWrap = tagReferenceBodyWrap.enter().append('div').attr('class', 'tag-reference-body-wrap').merge(tagReferenceBodyWrap); // update header
83901 if (_tagReference) {
83902 selection.selectAll('.preset-list-button-wrap .accessory-buttons').style('display', _presets.length === 1 ? null : 'none').call(_tagReference.button);
83903 tagReferenceBodyWrap.style('display', _presets.length === 1 ? null : 'none').call(_tagReference.body);
83906 selection.selectAll('.preset-reset').on('click', function () {
83907 dispatch$1.call('choose', this, _presets);
83908 }).on('pointerdown pointerup mousedown mouseup', function (d3_event) {
83909 d3_event.preventDefault();
83910 d3_event.stopPropagation();
83912 var geometries = entityGeometries();
83913 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')));
83914 var names = _presets.length === 1 ? [_presets[0].nameLabel(), _presets[0].subtitleLabel()].filter(Boolean) : [_t('inspector.multiple_types')];
83915 var label = selection.select('.label-inner');
83916 var nameparts = label.selectAll('.namepart').data(names, function (d) {
83919 nameparts.exit().remove();
83920 nameparts.enter().append('div').attr('class', 'namepart').html(function (d) {
83925 section.entityIDs = function (val) {
83926 if (!arguments.length) return _entityIDs;
83931 section.presets = function (val) {
83932 if (!arguments.length) return _presets; // don't reload the same preset
83934 if (!utilArrayIdentical(val, _presets)) {
83937 if (_presets.length === 1) {
83938 _tagReference = uiTagReference(_presets[0].reference()).showing(false);
83945 function entityGeometries() {
83948 for (var i in _entityIDs) {
83949 var geometry = context.graph().geometry(_entityIDs[i]);
83950 if (!counts[geometry]) counts[geometry] = 0;
83951 counts[geometry] += 1;
83954 return Object.keys(counts).sort(function (geom1, geom2) {
83955 return counts[geom2] - counts[geom1];
83959 return utilRebind(section, dispatch$1, 'on');
83962 // It borrows some code from uiHelp
83964 function uiFieldHelp(context, fieldName) {
83965 var fieldHelp = {};
83967 var _inspector = select(null);
83969 var _wrap = select(null);
83971 var _body = select(null);
83973 var fieldHelpKeys = {
83974 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']]]
83976 var fieldHelpHeadings = {};
83977 var replacements = {
83978 distField: _t.html('restriction.controls.distance'),
83979 viaField: _t.html('restriction.controls.via'),
83980 fromShadow: icon('#iD-turn-shadow', 'inline shadow from'),
83981 allowShadow: icon('#iD-turn-shadow', 'inline shadow allow'),
83982 restrictShadow: icon('#iD-turn-shadow', 'inline shadow restrict'),
83983 onlyShadow: icon('#iD-turn-shadow', 'inline shadow only'),
83984 allowTurn: icon('#iD-turn-yes', 'inline turn'),
83985 restrictTurn: icon('#iD-turn-no', 'inline turn'),
83986 onlyTurn: icon('#iD-turn-only', 'inline turn')
83987 }; // For each section, squash all the texts into a single markdown document
83989 var docs = fieldHelpKeys[fieldName].map(function (key) {
83990 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83991 var text = key[1].reduce(function (all, part) {
83992 var subkey = helpkey + '.' + part;
83993 var depth = fieldHelpHeadings[subkey]; // is this subkey a heading?
83995 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
83997 return all + hhh + _t.html(subkey, replacements) + '\n\n';
84001 title: _t.html(helpkey + '.title'),
84002 html: marked_1(text.trim())
84009 _body.classed('hide', false).style('opacity', '0').transition().duration(200).style('opacity', '1');
84013 _body.classed('hide', true).transition().duration(200).style('opacity', '0').on('end', function () {
84014 _body.classed('hide', true);
84018 function clickHelp(index) {
84019 var d = docs[index];
84020 var tkeys = fieldHelpKeys[fieldName][index][1];
84022 _body.selectAll('.field-help-nav-item').classed('active', function (d, i) {
84023 return i === index;
84026 var content = _body.selectAll('.field-help-content').html(d.html); // class the paragraphs so we can find and style them
84029 content.selectAll('p').attr('class', function (d, i) {
84031 }); // insert special content for certain help sections
84033 if (d.key === 'help.field.restrictions.inspecting') {
84034 content.insert('img', 'p.from_shadow').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_inspect.gif'));
84035 } else if (d.key === 'help.field.restrictions.modifying') {
84036 content.insert('img', 'p.allow_turn').attr('class', 'field-help-image cf').attr('src', context.imagePath('tr_modify.gif'));
84040 fieldHelp.button = function (selection) {
84041 if (_body.empty()) return;
84042 var button = selection.selectAll('.field-help-button').data([0]); // enter/update
84044 button.enter().append('button').attr('class', 'field-help-button').call(svgIcon('#iD-icon-help')).merge(button).on('click', function (d3_event) {
84045 d3_event.stopPropagation();
84046 d3_event.preventDefault();
84048 if (_body.classed('hide')) {
84056 function updatePosition() {
84057 var wrap = _wrap.node();
84059 var inspector = _inspector.node();
84061 var wRect = wrap.getBoundingClientRect();
84062 var iRect = inspector.getBoundingClientRect();
84064 _body.style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
84067 fieldHelp.body = function (selection) {
84068 // This control expects the field to have a form-field-input-wrap div
84069 _wrap = selection.selectAll('.form-field-input-wrap');
84070 if (_wrap.empty()) return; // absolute position relative to the inspector, so it "floats" above the fields
84072 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
84073 if (_inspector.empty()) return;
84074 _body = _inspector.selectAll('.field-help-body').data([0]);
84076 var enter = _body.enter().append('div').attr('class', 'field-help-body hide'); // initially hidden
84079 var titleEnter = enter.append('div').attr('class', 'field-help-title cf');
84080 titleEnter.append('h2').attr('class', _mainLocalizer.textDirection() === 'rtl' ? 'fr' : 'fl').html(_t.html('help.field.' + fieldName + '.title'));
84081 titleEnter.append('button').attr('class', 'fr close').on('click', function (d3_event) {
84082 d3_event.stopPropagation();
84083 d3_event.preventDefault();
84085 }).call(svgIcon('#iD-icon-close'));
84086 var navEnter = enter.append('div').attr('class', 'field-help-nav cf');
84087 var titles = docs.map(function (d) {
84090 navEnter.selectAll('.field-help-nav-item').data(titles).enter().append('div').attr('class', 'field-help-nav-item').html(function (d) {
84092 }).on('click', function (d3_event, d) {
84093 d3_event.stopPropagation();
84094 d3_event.preventDefault();
84095 clickHelp(titles.indexOf(d));
84097 enter.append('div').attr('class', 'field-help-content');
84098 _body = _body.merge(enter);
84105 function uiFieldCheck(field, context) {
84106 var dispatch$1 = dispatch('change');
84107 var options = field.strings && field.strings.options;
84113 var input = select(null);
84114 var text = select(null);
84115 var label = select(null);
84116 var reverser = select(null);
84120 var _entityIDs = [];
84125 for (var k in options) {
84126 values.push(k === 'undefined' ? undefined : k);
84127 texts.push(field.t.html('options.' + k, {
84128 'default': options[k]
84132 values = [undefined, 'yes'];
84133 texts = [_t.html('inspector.unknown'), _t.html('inspector.check.yes')];
84135 if (field.type !== 'defaultCheck') {
84137 texts.push(_t.html('inspector.check.no'));
84139 } // Checks tags to see whether an undefined value is "Assumed to be Yes"
84142 function checkImpliedYes() {
84143 _impliedYes = field.id === 'oneway_yes'; // hack: pretend `oneway` field is a `oneway_yes` field
84144 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
84146 if (field.id === 'oneway') {
84147 var entity = context.entity(_entityIDs[0]);
84149 for (var key in entity.tags) {
84150 if (key in osmOneWayTags && entity.tags[key] in osmOneWayTags[key]) {
84151 _impliedYes = true;
84152 texts[0] = _t.html('presets.fields.oneway_yes.options.undefined');
84159 function reverserHidden() {
84160 if (!context.container().select('div.inspector-hover').empty()) return true;
84161 return !(_value === 'yes' || _impliedYes && !_value);
84164 function reverserSetText(selection) {
84165 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84166 if (reverserHidden() || !entity) return selection;
84167 var first = entity.first();
84168 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
84169 var pseudoDirection = first < last;
84170 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
84171 selection.selectAll('.reverser-span').html(_t.html('inspector.check.reverser')).call(svgIcon(icon, 'inline'));
84175 var check = function check(selection) {
84177 label = selection.selectAll('.form-field-input-wrap').data([0]);
84178 var enter = label.enter().append('label').attr('class', 'form-field-input-wrap form-field-input-check');
84179 enter.append('input').property('indeterminate', field.type !== 'defaultCheck').attr('type', 'checkbox').attr('id', field.domId);
84180 enter.append('span').html(texts[0]).attr('class', 'value');
84182 if (field.type === 'onewayCheck') {
84183 enter.append('button').attr('class', 'reverser' + (reverserHidden() ? ' hide' : '')).append('span').attr('class', 'reverser-span');
84186 label = label.merge(enter);
84187 input = label.selectAll('input');
84188 text = label.selectAll('span.value');
84189 input.on('click', function (d3_event) {
84190 d3_event.stopPropagation();
84193 if (Array.isArray(_tags[field.key])) {
84194 if (values.indexOf('yes') !== -1) {
84195 t[field.key] = 'yes';
84197 t[field.key] = values[0];
84200 t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
84201 } // Don't cycle through `alternating` or `reversible` states - #4970
84202 // (They are supported as translated strings, but should not toggle with clicks)
84205 if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
84206 t[field.key] = values[0];
84209 dispatch$1.call('change', this, t);
84212 if (field.type === 'onewayCheck') {
84213 reverser = label.selectAll('.reverser');
84214 reverser.call(reverserSetText).on('click', function (d3_event) {
84215 d3_event.preventDefault();
84216 d3_event.stopPropagation();
84217 context.perform(function (graph) {
84218 for (var i in _entityIDs) {
84219 graph = actionReverse(_entityIDs[i])(graph);
84223 }, _t('operations.reverse.annotation.line', {
84225 })); // must manually revalidate since no 'change' event was called
84227 context.validator().validate();
84228 select(this).call(reverserSetText);
84233 check.entityIDs = function (val) {
84234 if (!arguments.length) return _entityIDs;
84239 check.tags = function (tags) {
84242 function isChecked(val) {
84243 return val !== 'no' && val !== '' && val !== undefined && val !== null;
84246 function textFor(val) {
84247 if (val === '') val = undefined;
84248 var index = values.indexOf(val);
84249 return index !== -1 ? texts[index] : '"' + val + '"';
84253 var isMixed = Array.isArray(tags[field.key]);
84254 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
84256 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
84260 input.property('indeterminate', isMixed || field.type !== 'defaultCheck' && !_value).property('checked', isChecked(_value));
84261 text.html(isMixed ? _t.html('inspector.multiple_values') : textFor(_value)).classed('mixed', isMixed);
84262 label.classed('set', !!_value);
84264 if (field.type === 'onewayCheck') {
84265 reverser.classed('hide', reverserHidden()).call(reverserSetText);
84269 check.focus = function () {
84270 input.node().focus();
84273 return utilRebind(check, dispatch$1, 'on');
84276 function uiFieldCombo(field, context) {
84277 var dispatch$1 = dispatch('change');
84279 var _isMulti = field.type === 'multiCombo' || field.type === 'manyCombo';
84281 var _isNetwork = field.type === 'networkCombo';
84283 var _isSemi = field.type === 'semiCombo';
84285 var _optstrings = field.strings && field.strings.options;
84287 var _optarray = field.options;
84289 var _snake_case = field.snake_case || field.snake_case === undefined;
84291 var _combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(field.caseSensitive).minItems(_isMulti || _isSemi ? 1 : 2);
84293 var _container = select(null);
84295 var _inputWrap = select(null);
84297 var _input = select(null);
84299 var _comboData = [];
84300 var _multiData = [];
84301 var _entityIDs = [];
84307 var _staticPlaceholder; // initialize deprecated tags array
84310 var _dataDeprecated = [];
84311 _mainFileFetcher.get('deprecated').then(function (d) {
84312 _dataDeprecated = d;
84313 })["catch"](function () {
84315 }); // ensure multiCombo field.key ends with a ':'
84317 if (_isMulti && field.key && /[^:]$/.test(field.key)) {
84321 function snake(s) {
84322 return s.replace(/\s+/g, '_');
84325 function unsnake(s) {
84326 return s.replace(/_+/g, ' ');
84329 function clean(s) {
84330 return s.split(';').map(function (s) {
84333 } // returns the tag value for a display value
84334 // (for multiCombo, dval should be the key suffix, not the entire key)
84337 function tagValue(dval) {
84338 dval = clean(dval || '');
84341 var found = _comboData.find(function (o) {
84342 return o.key && clean(o.value) === dval;
84350 if (field.type === 'typeCombo' && !dval) {
84354 return (_snake_case ? snake(dval) : dval) || undefined;
84355 } // returns the display value for a tag value
84356 // (for multiCombo, tval should be the key suffix, not the entire key)
84359 function displayValue(tval) {
84363 var found = _comboData.find(function (o) {
84364 return o.key === tval && o.value;
84368 return found.value;
84372 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
84376 return _snake_case ? unsnake(tval) : tval;
84377 } // Compute the difference between arrays of objects by `value` property
84379 // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
84380 // > [{value:1}, {value:3}]
84384 function objectDifference(a, b) {
84385 return a.filter(function (d1) {
84386 return !b.some(function (d2) {
84387 return !d2.isMixed && d1.value === d2.value;
84392 function initCombo(selection, attachTo) {
84394 selection.attr('readonly', 'readonly');
84395 selection.call(_combobox, attachTo);
84396 setStaticValues(setPlaceholder);
84397 } else if (_optarray) {
84398 selection.call(_combobox, attachTo);
84399 setStaticValues(setPlaceholder);
84400 } else if (services.taginfo) {
84401 selection.call(_combobox.fetcher(setTaginfoValues), attachTo);
84402 setTaginfoValues('', setPlaceholder);
84406 function setStaticValues(callback) {
84407 if (!(_optstrings || _optarray)) return;
84410 _comboData = Object.keys(_optstrings).map(function (k) {
84411 var v = field.t('options.' + k, {
84412 'default': _optstrings[k]
84418 display: field.t.html('options.' + k, {
84419 'default': _optstrings[k]
84423 } else if (_optarray) {
84424 _comboData = _optarray.map(function (k) {
84425 var v = _snake_case ? unsnake(k) : k;
84434 _combobox.data(objectDifference(_comboData, _multiData));
84436 if (callback) callback(_comboData);
84439 function setTaginfoValues(q, callback) {
84440 var fn = _isMulti ? 'multikeys' : 'values';
84441 var query = (_isMulti ? field.key : '') + q;
84442 var hasCountryPrefix = _isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
84444 if (hasCountryPrefix) {
84445 query = _countryCode + ':';
84449 debounce: q !== '',
84454 if (_entityIDs.length) {
84455 params.geometry = context.graph().geometry(_entityIDs[0]);
84458 services.taginfo[fn](params, function (err, data) {
84460 data = data.filter(function (d) {
84461 if (field.type === 'typeCombo' && d.value === 'yes') {
84462 // don't show the fallback value
84464 } // don't show values with very low usage
84467 return !d.count || d.count > 10;
84469 var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
84471 if (deprecatedValues) {
84472 // don't suggest deprecated tag values
84473 data = data.filter(function (d) {
84474 return deprecatedValues.indexOf(d.value) === -1;
84478 if (hasCountryPrefix) {
84479 data = data.filter(function (d) {
84480 return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
84482 } // hide the caret if there are no suggestions
84485 _container.classed('empty-combobox', data.length === 0);
84487 _comboData = data.map(function (d) {
84489 if (_isMulti) k = k.replace(field.key, '');
84490 var v = _snake_case ? unsnake(k) : k;
84494 title: _isMulti ? v : d.title
84497 _comboData = objectDifference(_comboData, _multiData);
84498 if (callback) callback(_comboData);
84502 function setPlaceholder(values) {
84503 if (_isMulti || _isSemi) {
84504 _staticPlaceholder = field.placeholder() || _t('inspector.add');
84506 var vals = values.map(function (d) {
84508 }).filter(function (s) {
84509 return s.length < 20;
84511 var placeholders = vals.length > 1 ? vals : values.map(function (d) {
84514 _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
84517 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
84518 _staticPlaceholder += '…';
84523 if (!_isMulti && !_isSemi && _tags && Array.isArray(_tags[field.key])) {
84524 ph = _t('inspector.multiple_values');
84526 ph = _staticPlaceholder;
84529 _container.selectAll('input').attr('placeholder', ph);
84532 function change() {
84536 if (_isMulti || _isSemi) {
84537 val = tagValue(utilGetSetValue(_input).replace(/,/g, ';')) || '';
84539 _container.classed('active', false);
84541 utilGetSetValue(_input, '');
84542 var vals = val.split(';').filter(Boolean);
84543 if (!vals.length) return;
84546 utilArrayUniq(vals).forEach(function (v) {
84547 var key = (field.key || '') + v;
84550 // don't set a multicombo value to 'yes' if it already has a non-'no' value
84551 // e.g. `language:de=main`
84552 var old = _tags[key];
84553 if (typeof old === 'string' && old.toLowerCase() !== 'no') return;
84556 key = context.cleanTagKey(key);
84557 field.keys.push(key);
84560 } else if (_isSemi) {
84561 var arr = _multiData.map(function (d) {
84565 arr = arr.concat(vals);
84566 t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
84569 window.setTimeout(function () {
84570 _input.node().focus();
84573 var rawValue = utilGetSetValue(_input); // don't override multiple values with blank string
84575 if (!rawValue && Array.isArray(_tags[field.key])) return;
84576 val = context.cleanTagValue(tagValue(rawValue));
84577 t[field.key] = val || undefined;
84580 dispatch$1.call('change', this, t);
84583 function removeMultikey(d3_event, d) {
84584 d3_event.preventDefault();
84585 d3_event.stopPropagation();
84589 t[d.key] = undefined;
84590 } else if (_isSemi) {
84591 var arr = _multiData.map(function (md) {
84592 return md.key === d.key ? null : md.key;
84593 }).filter(Boolean);
84595 arr = utilArrayUniq(arr);
84596 t[field.key] = arr.length ? arr.join(';') : undefined;
84599 dispatch$1.call('change', this, t);
84602 function combo(selection) {
84603 _container = selection.selectAll('.form-field-input-wrap').data([0]);
84604 var type = _isMulti || _isSemi ? 'multicombo' : 'combo';
84605 _container = _container.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + type).merge(_container);
84607 if (_isMulti || _isSemi) {
84608 _container = _container.selectAll('.chiplist').data([0]);
84609 var listClass = 'chiplist'; // Use a separate line for each value in the Destinations field
84610 // to mimic highway exit signs
84612 if (field.key === 'destination') {
84613 listClass += ' full-line-chips';
84616 _container = _container.enter().append('ul').attr('class', listClass).on('click', function () {
84617 window.setTimeout(function () {
84618 _input.node().focus();
84620 }).merge(_container);
84621 _inputWrap = _container.selectAll('.input-wrap').data([0]);
84622 _inputWrap = _inputWrap.enter().append('li').attr('class', 'input-wrap').merge(_inputWrap);
84623 _input = _inputWrap.selectAll('input').data([0]);
84625 _input = _container.selectAll('input').data([0]);
84628 _input = _input.enter().append('input').attr('type', 'text').attr('id', field.domId).call(utilNoAuto).call(initCombo, selection).merge(_input);
84631 var extent = combinedEntityExtent();
84632 var countryCode = extent && iso1A2Code(extent.center());
84633 _countryCode = countryCode && countryCode.toLowerCase();
84636 _input.on('change', change).on('blur', change);
84638 _input.on('keydown.field', function (d3_event) {
84639 switch (d3_event.keyCode) {
84642 _input.node().blur(); // blurring also enters the value
84645 d3_event.stopPropagation();
84650 if (_isMulti || _isSemi) {
84651 _combobox.on('accept', function () {
84652 _input.node().blur();
84654 _input.node().focus();
84657 _input.on('focus', function () {
84658 _container.classed('active', true);
84663 combo.tags = function (tags) {
84666 if (_isMulti || _isSemi) {
84671 // Build _multiData array containing keys already set..
84672 for (var k in tags) {
84673 if (field.key && k.indexOf(field.key) !== 0 || field.keys.indexOf(k) === -1) continue;
84675 if (!v || typeof v === 'string' && v.toLowerCase() === 'no') continue;
84676 var suffix = field.key ? k.substring(field.key.length) : k;
84680 value: displayValue(suffix),
84681 isMixed: Array.isArray(v)
84686 // Set keys for form-field modified (needed for undo and reset buttons)..
84687 field.keys = _multiData.map(function (d) {
84689 }); // limit the input length so it fits after prepending the key prefix
84691 maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84693 maxLength = context.maxCharsForTagKey();
84695 } else if (_isSemi) {
84696 var allValues = [];
84699 if (Array.isArray(tags[field.key])) {
84700 tags[field.key].forEach(function (tagVal) {
84701 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84702 allValues = allValues.concat(thisVals);
84704 if (!commonValues) {
84705 commonValues = thisVals;
84707 commonValues = commonValues.filter(function (value) {
84708 return thisVals.includes(value);
84712 allValues = utilArrayUniq(allValues).filter(Boolean);
84714 allValues = utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84715 commonValues = allValues;
84718 _multiData = allValues.map(function (v) {
84721 value: displayValue(v),
84722 isMixed: !commonValues.includes(v)
84725 var currLength = utilUnicodeCharsCount(commonValues.join(';')); // limit the input length to the remaining available characters
84727 maxLength = context.maxCharsForTagValue() - currLength;
84729 if (currLength > 0) {
84730 // account for the separator if a new value will be appended to existing
84733 } // a negative maxlength doesn't make sense
84736 maxLength = Math.max(0, maxLength);
84737 var allowDragAndDrop = _isSemi // only semiCombo values are ordered
84738 && !Array.isArray(tags[field.key]); // Exclude existing multikeys from combo options..
84740 var available = objectDifference(_comboData, _multiData);
84742 _combobox.data(available); // Hide 'Add' button if this field uses fixed set of
84743 // translateable _optstrings and they're all currently used,
84744 // or if the field is already at its character limit
84747 var hideAdd = _optstrings && !available.length || maxLength <= 0;
84749 _container.selectAll('.chiplist .input-wrap').style('display', hideAdd ? 'none' : null); // Render chips
84752 var chips = _container.selectAll('.chip').data(_multiData);
84754 chips.exit().remove();
84755 var enter = chips.enter().insert('li', '.input-wrap').attr('class', 'chip');
84756 enter.append('span');
84758 chips = chips.merge(enter).order().classed('draggable', allowDragAndDrop).classed('mixed', function (d) {
84760 }).attr('title', function (d) {
84761 return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84764 if (allowDragAndDrop) {
84765 registerDragAndDrop(chips);
84768 chips.select('span').html(function (d) {
84771 chips.select('a').attr('href', '#').on('click', removeMultikey).attr('class', 'remove').html('×');
84773 var isMixed = Array.isArray(tags[field.key]);
84774 var mixedValues = isMixed && tags[field.key].map(function (val) {
84775 return displayValue(val);
84776 }).filter(Boolean);
84777 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);
84781 function registerDragAndDrop(selection) {
84782 // allow drag and drop re-ordering of chips
84783 var dragOrigin, targetIndex;
84784 selection.call(d3_drag().on('start', function (d3_event) {
84789 targetIndex = null;
84790 }).on('drag', function (d3_event) {
84791 var x = d3_event.x - dragOrigin.x,
84792 y = d3_event.y - dragOrigin.y;
84793 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
84794 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
84795 var index = selection.nodes().indexOf(this);
84796 select(this).classed('dragging', true);
84797 targetIndex = null;
84798 var targetIndexOffsetTop = null;
84799 var draggedTagWidth = select(this).node().offsetWidth;
84801 if (field.key === 'destination') {
84802 // meaning tags are full width
84803 _container.selectAll('.chip').style('transform', function (d2, index2) {
84804 var node = select(this).node();
84806 if (index === index2) {
84807 return 'translate(' + x + 'px, ' + y + 'px)'; // move the dragged tag up the order
84808 } else if (index2 > index && d3_event.y > node.offsetTop) {
84809 if (targetIndex === null || index2 > targetIndex) {
84810 targetIndex = index2;
84813 return 'translateY(-100%)'; // move the dragged tag down the order
84814 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
84815 if (targetIndex === null || index2 < targetIndex) {
84816 targetIndex = index2;
84819 return 'translateY(100%)';
84825 _container.selectAll('.chip').each(function (d2, index2) {
84826 var node = select(this).node(); // check the cursor is in the bounding box
84828 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) {
84829 targetIndex = index2;
84830 targetIndexOffsetTop = node.offsetTop;
84832 }).style('transform', function (d2, index2) {
84833 var node = select(this).node();
84835 if (index === index2) {
84836 return 'translate(' + x + 'px, ' + y + 'px)';
84837 } // only translate tags in the same row
84840 if (node.offsetTop === targetIndexOffsetTop) {
84841 if (index2 < index && index2 >= targetIndex) {
84842 return 'translateX(' + draggedTagWidth + 'px)';
84843 } else if (index2 > index && index2 <= targetIndex) {
84844 return 'translateX(-' + draggedTagWidth + 'px)';
84851 }).on('end', function () {
84852 if (!select(this).classed('dragging')) {
84856 var index = selection.nodes().indexOf(this);
84857 select(this).classed('dragging', false);
84859 _container.selectAll('.chip').style('transform', null);
84861 if (typeof targetIndex === 'number') {
84862 var element = _multiData[index];
84864 _multiData.splice(index, 1);
84866 _multiData.splice(targetIndex, 0, element);
84870 if (_multiData.length) {
84871 t[field.key] = _multiData.map(function (element) {
84872 return element.key;
84875 t[field.key] = undefined;
84878 dispatch$1.call('change', this, t);
84881 dragOrigin = undefined;
84882 targetIndex = undefined;
84886 combo.focus = function () {
84887 _input.node().focus();
84890 combo.entityIDs = function (val) {
84891 if (!arguments.length) return _entityIDs;
84896 function combinedEntityExtent() {
84897 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84900 return utilRebind(combo, dispatch$1, 'on');
84903 function uiFieldText(field, context) {
84904 var dispatch$1 = dispatch('change');
84905 var input = select(null);
84906 var outlinkButton = select(null);
84907 var _entityIDs = [];
84911 var _phoneFormats = {};
84913 if (field.type === 'tel') {
84914 _mainFileFetcher.get('phone_formats').then(function (d) {
84916 updatePhonePlaceholder();
84917 })["catch"](function () {
84922 function i(selection) {
84923 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84924 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84925 var isLocked = preset && preset.suggestion && field.id === 'brand';
84926 field.locked(isLocked);
84927 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
84928 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
84929 input = wrap.selectAll('input').data([0]);
84930 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);
84931 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
84933 if (field.type === 'tel') {
84934 updatePhonePlaceholder();
84935 } else if (field.type === 'number') {
84936 var rtl = _mainLocalizer.textDirection() === 'rtl';
84937 input.attr('type', 'text');
84938 var inc = field.increment;
84939 var buttons = wrap.selectAll('.increment, .decrement').data(rtl ? [inc, -inc] : [-inc, inc]);
84940 buttons.enter().append('button').attr('class', function (d) {
84941 var which = d > 0 ? 'increment' : 'decrement';
84942 return 'form-field-button ' + which;
84943 }).merge(buttons).on('click', function (d3_event, d) {
84944 d3_event.preventDefault();
84945 var raw_vals = input.node().value || '0';
84946 var vals = raw_vals.split(';');
84947 vals = vals.map(function (v) {
84948 var num = parseFloat(v.trim(), 10);
84949 return isFinite(num) ? clamped(num + d) : v.trim();
84951 input.node().value = vals.join(';');
84954 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84955 input.attr('type', 'text');
84956 outlinkButton = wrap.selectAll('.foreign-id-permalink').data([0]);
84957 outlinkButton.enter().append('button').call(svgIcon('#iD-icon-out-link')).attr('class', 'form-field-button foreign-id-permalink').attr('title', function () {
84958 var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84960 if (domainResults.length >= 2 && domainResults[1]) {
84961 var domain = domainResults[1];
84962 return _t('icons.view_on', {
84968 }).on('click', function (d3_event) {
84969 d3_event.preventDefault();
84970 var value = validIdentifierValueForLink();
84973 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84974 window.open(url, '_blank');
84976 }).merge(outlinkButton);
84980 function updatePhonePlaceholder() {
84981 if (input.empty() || !Object.keys(_phoneFormats).length) return;
84982 var extent = combinedEntityExtent();
84983 var countryCode = extent && iso1A2Code(extent.center());
84985 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84987 if (format) input.attr('placeholder', format);
84990 function validIdentifierValueForLink() {
84991 if (field.type === 'identifier' && field.pattern) {
84992 var value = utilGetSetValue(input).trim().split(';')[0];
84993 return value && value.match(new RegExp(field.pattern));
84997 } // clamp number to min/max
85000 function clamped(num) {
85001 if (field.minValue !== undefined) {
85002 num = Math.max(num, field.minValue);
85005 if (field.maxValue !== undefined) {
85006 num = Math.min(num, field.maxValue);
85012 function change(onInput) {
85013 return function () {
85015 var val = utilGetSetValue(input);
85016 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
85018 if (!val && Array.isArray(_tags[field.key])) return;
85021 if (field.type === 'number' && val) {
85022 var vals = val.split(';');
85023 vals = vals.map(function (v) {
85024 var num = parseFloat(v.trim(), 10);
85025 return isFinite(num) ? clamped(num) : v.trim();
85027 val = vals.join(';');
85030 utilGetSetValue(input, val);
85033 t[field.key] = val || undefined;
85034 dispatch$1.call('change', this, t, onInput);
85038 i.entityIDs = function (val) {
85039 if (!arguments.length) return _entityIDs;
85044 i.tags = function (tags) {
85046 var isMixed = Array.isArray(tags[field.key]);
85047 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);
85049 if (outlinkButton && !outlinkButton.empty()) {
85050 var disabled = !validIdentifierValueForLink();
85051 outlinkButton.classed('disabled', disabled);
85055 i.focus = function () {
85056 var node = input.node();
85057 if (node) node.focus();
85060 function combinedEntityExtent() {
85061 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85064 return utilRebind(i, dispatch$1, 'on');
85067 function uiFieldAccess(field, context) {
85068 var dispatch$1 = dispatch('change');
85069 var items = select(null);
85073 function access(selection) {
85074 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85075 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85076 var list = wrap.selectAll('ul').data([0]);
85077 list = list.enter().append('ul').attr('class', 'rows').merge(list);
85078 items = list.selectAll('li').data(field.keys); // Enter
85080 var enter = items.enter().append('li').attr('class', function (d) {
85081 return 'labeled-input preset-access-' + d;
85083 enter.append('span').attr('class', 'label preset-label-access').attr('for', function (d) {
85084 return 'preset-input-access-' + d;
85085 }).html(function (d) {
85086 return field.t.html('types.' + d);
85088 enter.append('div').attr('class', 'preset-input-access-wrap').append('input').attr('type', 'text').attr('class', function (d) {
85089 return 'preset-input-access preset-input-access-' + d;
85090 }).call(utilNoAuto).each(function (d) {
85091 select(this).call(uiCombobox(context, 'access-' + d).data(access.options(d)));
85094 items = items.merge(enter);
85095 wrap.selectAll('.preset-input-access').on('change', change).on('blur', change);
85098 function change(d3_event, d) {
85100 var value = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
85102 if (!value && typeof _tags[d] !== 'string') return;
85103 tag[d] = value || undefined;
85104 dispatch$1.call('change', this, tag);
85107 access.options = function (type) {
85108 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
85110 if (type !== 'access') {
85111 options.unshift('yes');
85112 options.push('designated');
85114 if (type === 'bicycle') {
85115 options.push('dismount');
85119 return options.map(function (option) {
85121 title: field.t('options.' + option + '.description'),
85127 var placeholdersByHighway = {
85129 foot: 'designated',
85130 motor_vehicle: 'no'
85134 motor_vehicle: 'no',
85140 motor_vehicle: 'no'
85143 motor_vehicle: 'no',
85144 bicycle: 'designated'
85147 motor_vehicle: 'no',
85148 horse: 'designated'
85152 motor_vehicle: 'no',
85158 motor_vehicle: 'yes',
85163 motor_vehicle: 'yes'
85167 motor_vehicle: 'yes',
85173 motor_vehicle: 'yes',
85179 motor_vehicle: 'yes',
85185 motor_vehicle: 'yes',
85191 motor_vehicle: 'yes',
85197 motor_vehicle: 'yes',
85203 motor_vehicle: 'yes',
85208 motor_vehicle: 'yes'
85212 motor_vehicle: 'yes',
85218 motor_vehicle: 'yes',
85224 motor_vehicle: 'yes',
85230 access.tags = function (tags) {
85232 utilGetSetValue(items.selectAll('.preset-input-access'), function (d) {
85233 return typeof tags[d] === 'string' ? tags[d] : '';
85234 }).classed('mixed', function (d) {
85235 return tags[d] && Array.isArray(tags[d]);
85236 }).attr('title', function (d) {
85237 return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
85238 }).attr('placeholder', function (d) {
85239 if (tags[d] && Array.isArray(tags[d])) {
85240 return _t('inspector.multiple_values');
85243 if (d === 'access') {
85247 if (tags.access && typeof tags.access === 'string') {
85248 return tags.access;
85251 if (tags.highway) {
85252 if (typeof tags.highway === 'string') {
85253 if (placeholdersByHighway[tags.highway] && placeholdersByHighway[tags.highway][d]) {
85254 return placeholdersByHighway[tags.highway][d];
85257 var impliedAccesses = tags.highway.filter(Boolean).map(function (highwayVal) {
85258 return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
85259 }).filter(Boolean);
85261 if (impliedAccesses.length === tags.highway.length && new Set(impliedAccesses).size === 1) {
85262 // if all the highway values have the same implied access for this type then use that
85263 return impliedAccesses[0];
85268 return field.placeholder();
85272 access.focus = function () {
85273 items.selectAll('.preset-input-access').node().focus();
85276 return utilRebind(access, dispatch$1, 'on');
85279 function uiFieldAddress(field, context) {
85280 var dispatch$1 = dispatch('change');
85282 var _selection = select(null);
85284 var _wrap = select(null);
85286 var addrField = _mainPresetIndex.field('address'); // needed for placeholder strings
85288 var _entityIDs = [];
85294 var _addressFormats = [{
85295 format: [['housenumber', 'street'], ['city', 'postcode']]
85297 _mainFileFetcher.get('address_formats').then(function (d) {
85298 _addressFormats = d;
85300 if (!_selection.empty()) {
85301 _selection.call(address);
85303 })["catch"](function () {
85307 function getNearStreets() {
85308 var extent = combinedEntityExtent();
85309 var l = extent.center();
85310 var box = geoExtent(l).padByMeters(200);
85311 var streets = context.history().intersects(box).filter(isAddressable).map(function (d) {
85312 var loc = context.projection([(extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2]);
85313 var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
85315 title: d.tags.name,
85316 value: d.tags.name,
85317 dist: choice.distance
85319 }).sort(function (a, b) {
85320 return a.dist - b.dist;
85322 return utilArrayUniqBy(streets, 'value');
85324 function isAddressable(d) {
85325 return d.tags.highway && d.tags.name && d.type === 'way';
85329 function getNearCities() {
85330 var extent = combinedEntityExtent();
85331 var l = extent.center();
85332 var box = geoExtent(l).padByMeters(200);
85333 var cities = context.history().intersects(box).filter(isAddressable).map(function (d) {
85335 title: d.tags['addr:city'] || d.tags.name,
85336 value: d.tags['addr:city'] || d.tags.name,
85337 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
85339 }).sort(function (a, b) {
85340 return a.dist - b.dist;
85342 return utilArrayUniqBy(cities, 'value');
85344 function isAddressable(d) {
85346 if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative') return true;
85347 if (d.tags.border_type === 'city') return true;
85348 if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village') return true;
85351 if (d.tags['addr:city']) return true;
85356 function getNearValues(key) {
85357 var extent = combinedEntityExtent();
85358 var l = extent.center();
85359 var box = geoExtent(l).padByMeters(200);
85360 var results = context.history().intersects(box).filter(function hasTag(d) {
85361 return _entityIDs.indexOf(d.id) === -1 && d.tags[key];
85362 }).map(function (d) {
85364 title: d.tags[key],
85365 value: d.tags[key],
85366 dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
85368 }).sort(function (a, b) {
85369 return a.dist - b.dist;
85371 return utilArrayUniqBy(results, 'value');
85374 function updateForCountryCode() {
85375 if (!_countryCode) return;
85378 for (var i = 0; i < _addressFormats.length; i++) {
85379 var format = _addressFormats[i];
85381 if (!format.countryCodes) {
85382 addressFormat = format; // choose the default format, keep going
85383 } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
85384 addressFormat = format; // choose the country format, stop here
85390 var dropdowns = addressFormat.dropdowns || ['city', 'county', 'country', 'district', 'hamlet', 'neighbourhood', 'place', 'postcode', 'province', 'quarter', 'state', 'street', 'subdistrict', 'suburb'];
85391 var widths = addressFormat.widths || {
85392 housenumber: 1 / 3,
85400 // Normalize widths.
85401 var total = r.reduce(function (sum, key) {
85402 return sum + (widths[key] || 0.5);
85404 return r.map(function (key) {
85407 width: (widths[key] || 0.5) / total
85412 var rows = _wrap.selectAll('.addr-row').data(addressFormat.format, function (d) {
85413 return d.toString();
85416 rows.exit().remove();
85417 rows.enter().append('div').attr('class', 'addr-row').selectAll('input').data(row).enter().append('input').property('type', 'text').call(updatePlaceholder).attr('class', function (d) {
85418 return 'addr-' + d.id;
85419 }).call(utilNoAuto).each(addDropdown).style('width', function (d) {
85420 return d.width * 100 + '%';
85423 function addDropdown(d) {
85424 if (dropdowns.indexOf(d.id) === -1) return; // not a dropdown
85426 var nearValues = d.id === 'street' ? getNearStreets : d.id === 'city' ? getNearCities : getNearValues;
85427 select(this).call(uiCombobox(context, 'address-' + d.id).minItems(1).caseSensitive(true).fetcher(function (value, callback) {
85428 callback(nearValues('addr:' + d.id));
85432 _wrap.selectAll('input').on('blur', change()).on('change', change());
85434 _wrap.selectAll('input:not(.combobox-input)').on('input', change(true));
85436 if (_tags) updateTags(_tags);
85439 function address(selection) {
85440 _selection = selection;
85441 _wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85442 _wrap = _wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(_wrap);
85443 var extent = combinedEntityExtent();
85448 if (context.inIntro()) {
85449 // localize the address format for the walkthrough
85450 countryCode = _t('intro.graph.countrycode');
85452 var center = extent.center();
85453 countryCode = iso1A2Code(center);
85457 _countryCode = countryCode.toLowerCase();
85458 updateForCountryCode();
85463 function change(onInput) {
85464 return function () {
85467 _wrap.selectAll('input').each(function (subfield) {
85468 var key = field.key + ':' + subfield.id;
85469 var value = this.value;
85470 if (!onInput) value = context.cleanTagValue(value); // don't override multiple values with blank string
85472 if (Array.isArray(_tags[key]) && !value) return;
85473 tags[key] = value || undefined;
85476 dispatch$1.call('change', this, tags, onInput);
85480 function updatePlaceholder(inputSelection) {
85481 return inputSelection.attr('placeholder', function (subfield) {
85482 if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85483 return _t('inspector.multiple_values');
85486 if (_countryCode) {
85487 var localkey = subfield.id + '!' + _countryCode;
85488 var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85489 return addrField.t('placeholders.' + tkey);
85494 function updateTags(tags) {
85495 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85496 var val = tags[field.key + ':' + subfield.id];
85497 return typeof val === 'string' ? val : '';
85498 }).attr('title', function (subfield) {
85499 var val = tags[field.key + ':' + subfield.id];
85500 return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85501 }).classed('mixed', function (subfield) {
85502 return Array.isArray(tags[field.key + ':' + subfield.id]);
85503 }).call(updatePlaceholder);
85506 function combinedEntityExtent() {
85507 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85510 address.entityIDs = function (val) {
85511 if (!arguments.length) return _entityIDs;
85516 address.tags = function (tags) {
85521 address.focus = function () {
85522 var node = _wrap.selectAll('input').node();
85524 if (node) node.focus();
85527 return utilRebind(address, dispatch$1, 'on');
85530 function uiFieldCycleway(field, context) {
85531 var dispatch$1 = dispatch('change');
85532 var items = select(null);
85533 var wrap = select(null);
85537 function cycleway(selection) {
85538 function stripcolon(s) {
85539 return s.replace(':', '');
85542 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85543 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85544 var div = wrap.selectAll('ul').data([0]);
85545 div = div.enter().append('ul').attr('class', 'rows').merge(div);
85546 var keys = ['cycleway:left', 'cycleway:right'];
85547 items = div.selectAll('li').data(keys);
85548 var enter = items.enter().append('li').attr('class', function (d) {
85549 return 'labeled-input preset-cycleway-' + stripcolon(d);
85551 enter.append('span').attr('class', 'label preset-label-cycleway').attr('for', function (d) {
85552 return 'preset-input-cycleway-' + stripcolon(d);
85553 }).html(function (d) {
85554 return field.t.html('types.' + d);
85556 enter.append('div').attr('class', 'preset-input-cycleway-wrap').append('input').attr('type', 'text').attr('class', function (d) {
85557 return 'preset-input-cycleway preset-input-' + stripcolon(d);
85558 }).call(utilNoAuto).each(function (d) {
85559 select(this).call(uiCombobox(context, 'cycleway-' + stripcolon(d)).data(cycleway.options(d)));
85561 items = items.merge(enter); // Update
85563 wrap.selectAll('.preset-input-cycleway').on('change', change).on('blur', change);
85566 function change(d3_event, key) {
85567 var newValue = context.cleanTagValue(utilGetSetValue(select(this))); // don't override multiple values with blank string
85569 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) return;
85571 if (newValue === 'none' || newValue === '') {
85572 newValue = undefined;
85575 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85576 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85578 if (otherValue && Array.isArray(otherValue)) {
85579 // we must always have an explicit value for comparison
85580 otherValue = otherValue[0];
85583 if (otherValue === 'none' || otherValue === '') {
85584 otherValue = undefined;
85587 var tag = {}; // If the left and right tags match, use the cycleway tag to tag both
85588 // sides the same way
85590 if (newValue === otherValue) {
85592 cycleway: newValue,
85593 'cycleway:left': undefined,
85594 'cycleway:right': undefined
85597 // Always set both left and right as changing one can affect the other
85599 cycleway: undefined
85601 tag[key] = newValue;
85602 tag[otherKey] = otherValue;
85605 dispatch$1.call('change', this, tag);
85608 cycleway.options = function () {
85609 return Object.keys(field.strings.options).map(function (option) {
85611 title: field.t('options.' + option + '.description'),
85617 cycleway.tags = function (tags) {
85618 _tags = tags; // If cycleway is set, use that instead of individual values
85620 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85621 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function (d) {
85622 if (commonValue) return commonValue;
85623 return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85624 }).attr('title', function (d) {
85625 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85628 if (Array.isArray(tags.cycleway)) {
85629 vals = vals.concat(tags.cycleway);
85632 if (Array.isArray(tags[d])) {
85633 vals = vals.concat(tags[d]);
85636 return vals.filter(Boolean).join('\n');
85640 }).attr('placeholder', function (d) {
85641 if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85642 return _t('inspector.multiple_values');
85645 return field.placeholder();
85646 }).classed('mixed', function (d) {
85647 return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85651 cycleway.focus = function () {
85652 var node = wrap.selectAll('input').node();
85653 if (node) node.focus();
85656 return utilRebind(cycleway, dispatch$1, 'on');
85659 function uiFieldLanes(field, context) {
85660 var dispatch$1 = dispatch('change');
85661 var LANE_WIDTH = 40;
85662 var LANE_HEIGHT = 200;
85663 var _entityIDs = [];
85665 function lanes(selection) {
85666 var lanesData = context.entity(_entityIDs[0]).lanes();
85668 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85669 selection.call(lanes.off);
85673 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
85674 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85675 var surface = wrap.selectAll('.surface').data([0]);
85676 var d = utilGetDimensions(wrap);
85677 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85678 surface = surface.enter().append('svg').attr('width', d[0]).attr('height', 300).attr('class', 'surface').merge(surface);
85679 var lanesSelection = surface.selectAll('.lanes').data([0]);
85680 lanesSelection = lanesSelection.enter().append('g').attr('class', 'lanes').merge(lanesSelection);
85681 lanesSelection.attr('transform', function () {
85682 return 'translate(' + freeSpace / 2 + ', 0)';
85684 var lane = lanesSelection.selectAll('.lane').data(lanesData.lanes);
85685 lane.exit().remove();
85686 var enter = lane.enter().append('g').attr('class', 'lane');
85687 enter.append('g').append('rect').attr('y', 50).attr('width', LANE_WIDTH).attr('height', LANE_HEIGHT);
85688 enter.append('g').attr('class', 'forward').append('text').attr('y', 40).attr('x', 14).html('▲');
85689 enter.append('g').attr('class', 'bothways').append('text').attr('y', 40).attr('x', 14).html('▲▼');
85690 enter.append('g').attr('class', 'backward').append('text').attr('y', 40).attr('x', 14).html('▼');
85691 lane = lane.merge(enter);
85692 lane.attr('transform', function (d) {
85693 return 'translate(' + LANE_WIDTH * d.index * 1.5 + ', 0)';
85695 lane.select('.forward').style('visibility', function (d) {
85696 return d.direction === 'forward' ? 'visible' : 'hidden';
85698 lane.select('.bothways').style('visibility', function (d) {
85699 return d.direction === 'bothways' ? 'visible' : 'hidden';
85701 lane.select('.backward').style('visibility', function (d) {
85702 return d.direction === 'backward' ? 'visible' : 'hidden';
85706 lanes.entityIDs = function (val) {
85710 lanes.tags = function () {};
85712 lanes.focus = function () {};
85714 lanes.off = function () {};
85716 return utilRebind(lanes, dispatch$1, 'on');
85718 uiFieldLanes.supportsMultiselection = false;
85720 var _languagesArray = [];
85721 function uiFieldLocalized(field, context) {
85722 var dispatch$1 = dispatch('change', 'input');
85723 var wikipedia = services.wikipedia;
85724 var input = select(null);
85725 var localizedInputs = select(null);
85729 var _tags; // A concern here in switching to async data means that _languagesArray will not
85730 // be available the first time through, so things like the fetchers and
85731 // the language() function will not work immediately.
85734 _mainFileFetcher.get('languages').then(loadLanguagesArray)["catch"](function () {
85737 var _territoryLanguages = {};
85738 _mainFileFetcher.get('territory_languages').then(function (d) {
85739 _territoryLanguages = d;
85740 })["catch"](function () {
85743 var allSuggestions = _mainPresetIndex.collection.filter(function (p) {
85744 return p.suggestion === true;
85745 }); // reuse these combos
85747 var langCombo = uiCombobox(context, 'localized-lang').fetcher(fetchLanguages).minItems(0);
85748 var brandCombo = uiCombobox(context, 'localized-brand').canAutocomplete(false).minItems(1);
85750 var _selection = select(null);
85752 var _multilingual = [];
85754 var _buttonTip = uiTooltip().title(_t.html('translate.translate')).placement('left');
85758 var _entityIDs = [];
85760 function loadLanguagesArray(dataLanguages) {
85761 if (_languagesArray.length !== 0) return; // some conversion is needed to ensure correct OSM tags are used
85763 var replacements = {
85765 // in OSM, `sr` implies Cyrillic
85766 'sr-Cyrl': false // `sr-Cyrl` isn't used in OSM
85770 for (var code in dataLanguages) {
85771 if (replacements[code] === false) continue;
85772 var metaCode = code;
85773 if (replacements[code]) metaCode = replacements[code];
85775 _languagesArray.push({
85776 localName: _mainLocalizer.languageName(metaCode, {
85779 nativeName: dataLanguages[metaCode].nativeName,
85781 label: _mainLocalizer.languageName(metaCode)
85786 function calcLocked() {
85787 // only lock the Name field
85788 var isLocked = field.id === 'name' && _entityIDs.length && // lock the field if any feature needs it
85789 _entityIDs.some(function (entityID) {
85790 var entity = context.graph().hasEntity(entityID);
85791 if (!entity) return false;
85793 var original = context.graph().base().entities[_entityIDs[0]];
85795 var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name; // if the name was already edited manually then allow further editing
85797 if (!hasOriginalName) return false; // features linked to Wikidata are likely important and should be protected
85799 if (entity.tags.wikidata) return true; // assume the name has already been confirmed if its source has been researched
85801 if (entity.tags['name:etymology:wikidata']) return true;
85802 var preset = _mainPresetIndex.match(entity, context.graph());
85803 var isSuggestion = preset && preset.suggestion;
85804 var showsBrand = preset && preset.originalFields.filter(function (d) {
85805 return d.id === 'brand';
85806 }).length; // protect standardized brand names
85808 return isSuggestion && !showsBrand;
85811 field.locked(isLocked);
85812 } // update _multilingual, maintaining the existing order
85815 function calcMultilingual(tags) {
85816 var existingLangsOrdered = _multilingual.map(function (item) {
85820 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85822 for (var k in tags) {
85823 var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
85825 if (m && m[1] === field.key && m[2]) {
85831 if (existingLangs.has(item.lang)) {
85832 // update the value
85833 _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85834 existingLangs["delete"](item.lang);
85836 _multilingual.push(item);
85841 _multilingual = _multilingual.filter(function (item) {
85842 return !item.lang || !existingLangs.has(item.lang);
85846 function localized(selection) {
85847 _selection = selection;
85849 var isLocked = field.locked();
85850 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85851 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85852 var wrap = selection.selectAll('.form-field-input-wrap').data([0]); // enter/update
85854 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
85855 input = wrap.selectAll('.localized-main').data([0]); // enter/update
85857 input = input.enter().append('input').attr('type', 'text').attr('id', field.domId).attr('class', 'localized-main').call(utilNoAuto).merge(input);
85859 if (preset && field.id === 'name') {
85860 var pTag = preset.id.split('/', 2);
85861 var pKey = pTag[0];
85862 var pValue = pTag[1];
85864 if (!preset.suggestion) {
85865 // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85866 // This code attempts to determine if the matched preset is the
85867 // kind of preset that even can benefit from name suggestions..
85868 // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85869 // - false = churches, parks, hospitals, etc. (things not in the index)
85870 var isFallback = preset.isFallback();
85871 var goodSuggestions = allSuggestions.filter(function (s) {
85872 if (isFallback) return true;
85873 var sTag = s.id.split('/', 2);
85874 var sKey = sTag[0];
85875 var sValue = sTag[1];
85876 return pKey === sKey && (!pValue || pValue === sValue);
85877 }); // Show the suggestions.. If the user picks one, change the tags..
85879 if (allSuggestions.length && goodSuggestions.length) {
85880 input.on('blur.localized', checkBrandOnBlur).call(brandCombo.fetcher(fetchBrandNames(preset, allSuggestions)).on('accept', acceptBrand).on('cancel', cancelBrand));
85885 input.classed('disabled', !!isLocked).attr('readonly', isLocked || null).on('input', change(true)).on('blur', change()).on('change', change());
85886 var translateButton = wrap.selectAll('.localized-add').data([0]);
85887 translateButton = translateButton.enter().append('button').attr('class', 'localized-add form-field-button').call(svgIcon('#iD-icon-plus')).merge(translateButton);
85888 translateButton.classed('disabled', !!isLocked).call(isLocked ? _buttonTip.destroy : _buttonTip).on('click', addNew);
85890 if (_tags && !_multilingual.length) {
85891 calcMultilingual(_tags);
85894 localizedInputs = selection.selectAll('.localized-multilingual').data([0]);
85895 localizedInputs = localizedInputs.enter().append('div').attr('class', 'localized-multilingual').merge(localizedInputs);
85896 localizedInputs.call(renderMultilingual);
85897 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.
85898 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85899 // So compare the current field value against the suggestions one last time.
85901 function checkBrandOnBlur() {
85902 var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85903 if (!latest) return; // deleting the entity blurred the field?
85905 var preset = _mainPresetIndex.match(latest, context.graph());
85906 if (preset && preset.suggestion) return; // already accepted
85908 var name = utilGetSetValue(input).trim();
85909 var matched = allSuggestions.filter(function (s) {
85910 return name === s.name();
85913 if (matched.length === 1) {
85915 suggestion: matched[0]
85922 function acceptBrand(d) {
85923 var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85925 if (!d || !entity) {
85930 var tags = entity.tags;
85931 var geometry = entity.geometry(context.graph());
85932 var removed = preset.unsetTags(tags, geometry);
85934 for (var k in tags) {
85935 tags[k] = removed[k]; // set removed tags to `undefined`
85938 tags = d.suggestion.setTags(tags, geometry);
85939 utilGetSetValue(input, tags.name);
85940 dispatch$1.call('change', this, tags);
85941 } // user hit escape
85944 function cancelBrand() {
85945 var name = utilGetSetValue(input);
85946 dispatch$1.call('change', this, {
85951 function fetchBrandNames(preset, suggestions) {
85952 var pTag = preset.id.split('/', 2);
85953 var pKey = pTag[0];
85954 var pValue = pTag[1];
85955 return function (value, callback) {
85958 if (value && value.length > 2) {
85959 for (var i = 0; i < suggestions.length; i++) {
85960 var s = suggestions[i]; // don't suggest brands from incompatible countries
85962 if (_countryCode && s.countryCodes && s.countryCodes.indexOf(_countryCode) === -1) continue;
85963 var sTag = s.id.split('/', 2);
85964 var sKey = sTag[0];
85965 var sValue = sTag[1];
85966 var subtitle = s.subtitle();
85967 var name = s.name();
85968 if (subtitle) name += ' – ' + subtitle;
85969 var dist = utilEditDistance(value, name.substring(0, value.length));
85970 var matchesPreset = pKey === sKey && (!pValue || pValue === sValue);
85972 if (dist < 1 || matchesPreset && dist < 3) {
85976 display: s.nameLabel() + (subtitle ? ' – ' + s.subtitleLabel() : ''),
85978 dist: dist + (matchesPreset ? 0 : 1) // penalize if not matched preset
85985 results.sort(function (a, b) {
85986 return a.dist - b.dist;
85990 results = results.slice(0, 10);
85995 function addNew(d3_event) {
85996 d3_event.preventDefault();
85997 if (field.locked()) return;
85998 var defaultLang = _mainLocalizer.languageCode().toLowerCase();
86000 var langExists = _multilingual.find(function (datum) {
86001 return datum.lang === defaultLang;
86004 var isLangEn = defaultLang.indexOf('en') > -1;
86006 if (isLangEn || langExists) {
86008 langExists = _multilingual.find(function (datum) {
86009 return datum.lang === defaultLang;
86014 // prepend the value so it appears at the top
86015 _multilingual.unshift({
86020 localizedInputs.call(renderMultilingual);
86024 function change(onInput) {
86025 return function (d3_event) {
86026 if (field.locked()) {
86027 d3_event.preventDefault();
86031 var val = utilGetSetValue(select(this));
86032 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
86034 if (!val && Array.isArray(_tags[field.key])) return;
86036 t[field.key] = val || undefined;
86037 dispatch$1.call('change', this, t, onInput);
86042 function key(lang) {
86043 return field.key + ':' + lang;
86046 function changeLang(d3_event, d) {
86047 var tags = {}; // make sure unrecognized suffixes are lowercase - #7156
86049 var lang = utilGetSetValue(select(this)).toLowerCase();
86051 var language = _languagesArray.find(function (d) {
86052 return d.label.toLowerCase() === lang || d.localName && d.localName.toLowerCase() === lang || d.nativeName && d.nativeName.toLowerCase() === lang;
86055 if (language) lang = language.code;
86057 if (d.lang && d.lang !== lang) {
86058 tags[key(d.lang)] = undefined;
86061 var newKey = lang && context.cleanTagKey(key(lang));
86062 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
86064 if (newKey && value) {
86065 tags[newKey] = value;
86066 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
86067 tags[newKey] = _wikiTitles[d.lang];
86071 dispatch$1.call('change', this, tags);
86074 function changeValue(d3_event, d) {
86075 if (!d.lang) return;
86076 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined; // don't override multiple values with blank string
86078 if (!value && Array.isArray(d.value)) return;
86080 t[key(d.lang)] = value;
86082 dispatch$1.call('change', this, t);
86085 function fetchLanguages(value, cb) {
86086 var v = value.toLowerCase(); // show the user's language first
86088 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
86090 if (_countryCode && _territoryLanguages[_countryCode]) {
86091 langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
86094 var langItems = [];
86095 langCodes.forEach(function (code) {
86096 var langItem = _languagesArray.find(function (item) {
86097 return item.code === code;
86100 if (langItem) langItems.push(langItem);
86102 langItems = utilArrayUniq(langItems.concat(_languagesArray));
86103 cb(langItems.filter(function (d) {
86104 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;
86105 }).map(function (d) {
86112 function renderMultilingual(selection) {
86113 var entries = selection.selectAll('div.entry').data(_multilingual, function (d) {
86116 entries.exit().style('top', '0').style('max-height', '240px').transition().duration(200).style('opacity', '0').style('max-height', '0px').remove();
86117 var entriesEnter = entries.enter().append('div').attr('class', 'entry').each(function (_, index) {
86118 var wrap = select(this);
86119 var domId = utilUniqueDomId(index);
86120 var label = wrap.append('label').attr('class', 'field-label').attr('for', domId);
86121 var text = label.append('span').attr('class', 'label-text');
86122 text.append('span').attr('class', 'label-textvalue').html(_t.html('translate.localized_translation_label'));
86123 text.append('span').attr('class', 'label-textannotation');
86124 label.append('button').attr('class', 'remove-icon-multilingual').on('click', function (d3_event, d) {
86125 if (field.locked()) return;
86126 d3_event.preventDefault();
86128 if (!d.lang || !d.value) {
86129 _multilingual.splice(index, 1);
86131 renderMultilingual(selection);
86133 // remove from entity tags
86135 t[key(d.lang)] = undefined;
86136 dispatch$1.call('change', this, t);
86138 }).call(svgIcon('#iD-operation-delete'));
86139 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);
86140 wrap.append('input').attr('type', 'text').attr('class', 'localized-value').on('blur', changeValue).on('change', changeValue);
86142 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 () {
86143 select(this).style('max-height', '').style('overflow', 'visible');
86145 entries = entries.merge(entriesEnter);
86147 entries.classed('present', function (d) {
86148 return d.lang && d.value;
86150 utilGetSetValue(entries.select('.localized-lang'), function (d) {
86151 return _mainLocalizer.languageName(d.lang);
86153 utilGetSetValue(entries.select('.localized-value'), function (d) {
86154 return typeof d.value === 'string' ? d.value : '';
86155 }).attr('title', function (d) {
86156 return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
86157 }).attr('placeholder', function (d) {
86158 return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
86159 }).classed('mixed', function (d) {
86160 return Array.isArray(d.value);
86164 localized.tags = function (tags) {
86165 _tags = tags; // Fetch translations from wikipedia
86167 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
86169 var wm = tags.wikipedia.match(/([^:]+):(.+)/);
86171 if (wm && wm[0] && wm[1]) {
86172 wikipedia.translations(wm[1], wm[2], function (err, d) {
86173 if (err || !d) return;
86179 var isMixed = Array.isArray(tags[field.key]);
86180 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);
86181 calcMultilingual(tags);
86183 _selection.call(localized);
86186 localized.focus = function () {
86187 input.node().focus();
86190 localized.entityIDs = function (val) {
86191 if (!arguments.length) return _entityIDs;
86193 _multilingual = [];
86198 function loadCountryCode() {
86199 var extent = combinedEntityExtent();
86200 var countryCode = extent && iso1A2Code(extent.center());
86201 _countryCode = countryCode && countryCode.toLowerCase();
86204 function combinedEntityExtent() {
86205 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86208 return utilRebind(localized, dispatch$1, 'on');
86211 function uiFieldMaxspeed(field, context) {
86212 var dispatch$1 = dispatch('change');
86213 var unitInput = select(null);
86214 var input = select(null);
86215 var _entityIDs = [];
86221 var speedCombo = uiCombobox(context, 'maxspeed');
86222 var unitCombo = uiCombobox(context, 'maxspeed-unit').data(['km/h', 'mph'].map(comboValues));
86223 var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86224 var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86226 function maxspeed(selection) {
86227 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86228 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86229 input = wrap.selectAll('input.maxspeed-number').data([0]);
86230 input = input.enter().append('input').attr('type', 'text').attr('class', 'maxspeed-number').attr('id', field.domId).call(utilNoAuto).call(speedCombo).merge(input);
86231 input.on('change', change).on('blur', change);
86232 var loc = combinedEntityExtent().center();
86233 _isImperial = roadSpeedUnit(loc) === 'mph';
86234 unitInput = wrap.selectAll('input.maxspeed-unit').data([0]);
86235 unitInput = unitInput.enter().append('input').attr('type', 'text').attr('class', 'maxspeed-unit').call(unitCombo).merge(unitInput);
86236 unitInput.on('blur', changeUnits).on('change', changeUnits);
86238 function changeUnits() {
86239 _isImperial = utilGetSetValue(unitInput) === 'mph';
86240 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86241 setUnitSuggestions();
86246 function setUnitSuggestions() {
86247 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86248 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86251 function comboValues(d) {
86253 value: d.toString(),
86254 title: d.toString()
86258 function change() {
86260 var value = utilGetSetValue(input).trim(); // don't override multiple values with blank string
86262 if (!value && Array.isArray(_tags[field.key])) return;
86265 tag[field.key] = undefined;
86266 } else if (isNaN(value) || !_isImperial) {
86267 tag[field.key] = context.cleanTagValue(value);
86269 tag[field.key] = context.cleanTagValue(value + ' mph');
86272 dispatch$1.call('change', this, tag);
86275 maxspeed.tags = function (tags) {
86277 var value = tags[field.key];
86278 var isMixed = Array.isArray(value);
86281 if (value && value.indexOf('mph') >= 0) {
86282 value = parseInt(value, 10).toString();
86283 _isImperial = true;
86284 } else if (value) {
86285 _isImperial = false;
86289 setUnitSuggestions();
86290 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);
86293 maxspeed.focus = function () {
86294 input.node().focus();
86297 maxspeed.entityIDs = function (val) {
86301 function combinedEntityExtent() {
86302 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86305 return utilRebind(maxspeed, dispatch$1, 'on');
86308 function uiFieldRadio(field, context) {
86309 var dispatch$1 = dispatch('change');
86310 var placeholder = select(null);
86311 var wrap = select(null);
86312 var labels = select(null);
86313 var radios = select(null);
86314 var radioData = (field.options || field.strings && field.strings.options && Object.keys(field.strings.options) || field.keys).slice(); // shallow copy
86319 var _entityIDs = [];
86321 function selectedKey() {
86322 var node = wrap.selectAll('.form-field-input-radio label.active input');
86323 return !node.empty() && node.datum();
86326 function radio(selection) {
86327 selection.classed('preset-radio', true);
86328 wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86329 var enter = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-radio');
86330 enter.append('span').attr('class', 'placeholder');
86331 wrap = wrap.merge(enter);
86332 placeholder = wrap.selectAll('.placeholder');
86333 labels = wrap.selectAll('label').data(radioData);
86334 enter = labels.enter().append('label');
86335 enter.append('input').attr('type', 'radio').attr('name', field.id).attr('value', function (d) {
86336 return field.t('options.' + d, {
86339 }).attr('checked', false);
86340 enter.append('span').html(function (d) {
86341 return field.t.html('options.' + d, {
86345 labels = labels.merge(enter);
86346 radios = labels.selectAll('input').on('change', changeRadio);
86349 function structureExtras(selection, tags) {
86350 var selected = selectedKey() || tags.layer !== undefined;
86351 var type = _mainPresetIndex.field(selected);
86352 var layer = _mainPresetIndex.field('layer');
86353 var showLayer = selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined;
86354 var extrasWrap = selection.selectAll('.structure-extras-wrap').data(selected ? [0] : []);
86355 extrasWrap.exit().remove();
86356 extrasWrap = extrasWrap.enter().append('div').attr('class', 'structure-extras-wrap').merge(extrasWrap);
86357 var list = extrasWrap.selectAll('ul').data([0]);
86358 list = list.enter().append('ul').attr('class', 'rows').merge(list); // Type
86361 if (!typeField || typeField.id !== selected) {
86362 typeField = uiField(context, type, _entityIDs, {
86364 }).on('change', changeType);
86367 typeField.tags(tags);
86372 var typeItem = list.selectAll('.structure-type-item').data(typeField ? [typeField] : [], function (d) {
86376 typeItem.exit().remove(); // Enter
86378 var typeEnter = typeItem.enter().insert('li', ':first-child').attr('class', 'labeled-input structure-type-item');
86379 typeEnter.append('span').attr('class', 'label structure-label-type').attr('for', 'preset-input-' + selected).html(_t.html('inspector.radio.structure.type'));
86380 typeEnter.append('div').attr('class', 'structure-input-type-wrap'); // Update
86382 typeItem = typeItem.merge(typeEnter);
86385 typeItem.selectAll('.structure-input-type-wrap').call(typeField.render);
86389 if (layer && showLayer) {
86391 layerField = uiField(context, layer, _entityIDs, {
86393 }).on('change', changeLayer);
86396 layerField.tags(tags);
86397 field.keys = utilArrayUnion(field.keys, ['layer']);
86400 field.keys = field.keys.filter(function (k) {
86401 return k !== 'layer';
86405 var layerItem = list.selectAll('.structure-layer-item').data(layerField ? [layerField] : []); // Exit
86407 layerItem.exit().remove(); // Enter
86409 var layerEnter = layerItem.enter().append('li').attr('class', 'labeled-input structure-layer-item');
86410 layerEnter.append('span').attr('class', 'label structure-label-layer').attr('for', 'preset-input-layer').html(_t.html('inspector.radio.structure.layer'));
86411 layerEnter.append('div').attr('class', 'structure-input-layer-wrap'); // Update
86413 layerItem = layerItem.merge(layerEnter);
86416 layerItem.selectAll('.structure-input-layer-wrap').call(layerField.render);
86420 function changeType(t, onInput) {
86421 var key = selectedKey();
86425 if (val !== 'no') {
86426 _oldType[key] = val;
86429 if (field.type === 'structureRadio') {
86430 // remove layer if it should not be set
86431 if (val === 'no' || key !== 'bridge' && key !== 'tunnel' || key === 'tunnel' && val === 'building_passage') {
86432 t.layer = undefined;
86433 } // add layer if it should be set
86436 if (t.layer === undefined) {
86437 if (key === 'bridge' && val !== 'no') {
86441 if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86447 dispatch$1.call('change', this, t, onInput);
86450 function changeLayer(t, onInput) {
86451 if (t.layer === '0') {
86452 t.layer = undefined;
86455 dispatch$1.call('change', this, t, onInput);
86458 function changeRadio() {
86463 t[field.key] = undefined;
86466 radios.each(function (d) {
86467 var active = select(this).property('checked');
86468 if (active) activeKey = d;
86471 if (active) t[field.key] = d;
86473 var val = _oldType[activeKey] || 'yes';
86474 t[d] = active ? val : undefined;
86478 if (field.type === 'structureRadio') {
86479 if (activeKey === 'bridge') {
86481 } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86484 t.layer = undefined;
86488 dispatch$1.call('change', this, t);
86491 radio.tags = function (tags) {
86492 radios.property('checked', function (d) {
86494 return tags[field.key] === d;
86497 return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86500 function isMixed(d) {
86502 return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86505 return Array.isArray(tags[d]);
86508 labels.classed('active', function (d) {
86510 return Array.isArray(tags[field.key]) && tags[field.key].includes(d) || tags[field.key] === d;
86513 return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86514 }).classed('mixed', isMixed).attr('title', function (d) {
86515 return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86517 var selection = radios.filter(function () {
86518 return this.checked;
86521 if (selection.empty()) {
86522 placeholder.html(_t.html('inspector.none'));
86524 placeholder.html(selection.attr('value'));
86525 _oldType[selection.datum()] = tags[selection.datum()];
86528 if (field.type === 'structureRadio') {
86529 // For waterways without a tunnel tag, set 'culvert' as
86530 // the _oldType to default to if the user picks 'tunnel'
86531 if (!!tags.waterway && !_oldType.tunnel) {
86532 _oldType.tunnel = 'culvert';
86535 wrap.call(structureExtras, tags);
86539 radio.focus = function () {
86540 radios.node().focus();
86543 radio.entityIDs = function (val) {
86544 if (!arguments.length) return _entityIDs;
86550 radio.isAllowed = function () {
86551 return _entityIDs.length === 1;
86554 return utilRebind(radio, dispatch$1, 'on');
86557 function uiFieldRestrictions(field, context) {
86558 var dispatch$1 = dispatch('change');
86559 var breathe = behaviorBreathe();
86560 corePreferences('turn-restriction-via-way', null); // remove old key
86562 var storedViaWay = corePreferences('turn-restriction-via-way0'); // use new key #6922
86564 var storedDistance = corePreferences('turn-restriction-distance');
86566 var _maxViaWay = storedViaWay !== null ? +storedViaWay : 0;
86568 var _maxDistance = storedDistance ? +storedDistance : 30;
86570 var _initialized = false;
86572 var _parent = select(null); // the entire field
86575 var _container = select(null); // just the map
86590 function restrictions(selection) {
86591 _parent = selection; // try to reuse the intersection, but always rebuild it if the graph has changed
86593 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86594 _graph = context.graph();
86595 _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86596 } // It's possible for there to be no actual intersection here.
86597 // for example, a vertex of two `highway=path`
86598 // In this case, hide the field.
86601 var isOK = _intersection && _intersection.vertices.length && // has vertices
86602 _intersection.vertices // has the vertex that the user selected
86603 .filter(function (vertex) {
86604 return vertex.id === _vertexID;
86605 }).length && _intersection.ways.length > 2 && // has more than 2 ways
86606 _intersection.ways // has more than 1 TO way
86607 .filter(function (way) {
86609 }).length > 1; // Also hide in the case where
86611 select(selection.node().parentNode).classed('hide', !isOK); // if form field is hidden or has detached from dom, clean up.
86613 if (!isOK || !context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode || !selection.node().parentNode.parentNode) {
86614 selection.call(restrictions.off);
86618 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
86619 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
86620 var container = wrap.selectAll('.restriction-container').data([0]); // enter
86622 var containerEnter = container.enter().append('div').attr('class', 'restriction-container');
86623 containerEnter.append('div').attr('class', 'restriction-help'); // update
86625 _container = containerEnter.merge(container).call(renderViewer);
86626 var controls = wrap.selectAll('.restriction-controls').data([0]); // enter/update
86628 controls.enter().append('div').attr('class', 'restriction-controls-container').append('div').attr('class', 'restriction-controls').merge(controls).call(renderControls);
86631 function renderControls(selection) {
86632 var distControl = selection.selectAll('.restriction-distance').data([0]);
86633 distControl.exit().remove();
86634 var distControlEnter = distControl.enter().append('div').attr('class', 'restriction-control restriction-distance');
86635 distControlEnter.append('span').attr('class', 'restriction-control-label restriction-distance-label').html(_t.html('restriction.controls.distance') + ':');
86636 distControlEnter.append('input').attr('class', 'restriction-distance-input').attr('type', 'range').attr('min', '20').attr('max', '50').attr('step', '5');
86637 distControlEnter.append('span').attr('class', 'restriction-distance-text'); // update
86639 selection.selectAll('.restriction-distance-input').property('value', _maxDistance).on('input', function () {
86640 var val = select(this).property('value');
86641 _maxDistance = +val;
86642 _intersection = null;
86644 _container.selectAll('.layer-osm .layer-turns *').remove();
86646 corePreferences('turn-restriction-distance', _maxDistance);
86648 _parent.call(restrictions);
86650 selection.selectAll('.restriction-distance-text').html(displayMaxDistance(_maxDistance));
86651 var viaControl = selection.selectAll('.restriction-via-way').data([0]);
86652 viaControl.exit().remove();
86653 var viaControlEnter = viaControl.enter().append('div').attr('class', 'restriction-control restriction-via-way');
86654 viaControlEnter.append('span').attr('class', 'restriction-control-label restriction-via-way-label').html(_t.html('restriction.controls.via') + ':');
86655 viaControlEnter.append('input').attr('class', 'restriction-via-way-input').attr('type', 'range').attr('min', '0').attr('max', '2').attr('step', '1');
86656 viaControlEnter.append('span').attr('class', 'restriction-via-way-text'); // update
86658 selection.selectAll('.restriction-via-way-input').property('value', _maxViaWay).on('input', function () {
86659 var val = select(this).property('value');
86662 _container.selectAll('.layer-osm .layer-turns *').remove();
86664 corePreferences('turn-restriction-via-way0', _maxViaWay);
86666 _parent.call(restrictions);
86668 selection.selectAll('.restriction-via-way-text').html(displayMaxVia(_maxViaWay));
86671 function renderViewer(selection) {
86672 if (!_intersection) return;
86673 var vgraph = _intersection.graph;
86674 var filter = utilFunctor(true);
86675 var projection = geoRawMercator(); // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86676 // Instead of asking the restriction-container for its dimensions,
86677 // we can ask the .sidebar, which can have its dimensions cached.
86678 // width: calc as sidebar - padding
86679 // height: hardcoded (from `80_app.css`)
86680 // var d = utilGetDimensions(selection);
86682 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86683 var d = [sdims[0] - 50, 370];
86684 var c = geoVecScale(d, 0.5);
86686 projection.scale(geoZoomToScale(z)); // Calculate extent of all key vertices
86688 var extent = geoExtent();
86690 for (var i = 0; i < _intersection.vertices.length; i++) {
86691 extent._extend(_intersection.vertices[i].extent());
86692 } // If this is a large intersection, adjust zoom to fit extent
86695 if (_intersection.vertices.length > 1) {
86696 var padding = 180; // in z22 pixels
86698 var tl = projection([extent[0][0], extent[1][1]]);
86699 var br = projection([extent[1][0], extent[0][1]]);
86700 var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86701 var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86702 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86703 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86704 z = z - Math.max(hZoomDiff, vZoomDiff);
86705 projection.scale(geoZoomToScale(z));
86708 var padTop = 35; // reserve top space for hint text
86710 var extentCenter = projection(extent.center());
86711 extentCenter[1] = extentCenter[1] - padTop;
86712 projection.translate(geoVecSubtract(c, extentCenter)).clipExtent([[0, 0], d]);
86713 var drawLayers = svgLayers(projection, context).only(['osm', 'touch']).dimensions(d);
86714 var drawVertices = svgVertices(projection, context);
86715 var drawLines = svgLines(projection, context);
86716 var drawTurns = svgTurns(projection, context);
86717 var firstTime = selection.selectAll('.surface').empty();
86718 selection.call(drawLayers);
86719 var surface = selection.selectAll('.surface').classed('tr', true);
86722 _initialized = true;
86723 surface.call(breathe);
86724 } // This can happen if we've lowered the detail while a FROM way
86725 // is selected, and that way is no longer part of the intersection.
86728 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86733 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));
86734 surface.on('click.restrictions', click).on('mouseover.restrictions', mouseover);
86735 surface.selectAll('.selected').classed('selected', false);
86736 surface.selectAll('.related').classed('related', false);
86740 way = vgraph.entity(_fromWayID);
86741 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
86744 document.addEventListener('resizeWindow', function () {
86745 utilSetDimensions(_container, null);
86750 function click(d3_event) {
86751 surface.call(breathe.off).call(breathe);
86752 var datum = d3_event.target.__data__;
86753 var entity = datum && datum.properties && datum.properties.entity;
86759 if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86760 _fromWayID = datum.id;
86763 } else if (datum instanceof osmTurn) {
86764 var actions, extraActions, turns, i;
86765 var restrictionType = osmInferRestriction(vgraph, datum, projection);
86767 if (datum.restrictionID && !datum.direct) {
86769 } else if (datum.restrictionID && !datum.only) {
86772 var datumOnly = JSON.parse(JSON.stringify(datum)); // deep clone the datum
86774 datumOnly.only = true; // but change this property
86776 restrictionType = restrictionType.replace(/^no/, 'only'); // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86777 // We will remember them in _oldTurns, and restore them if the user clicks again.
86779 turns = _intersection.turns(_fromWayID, 2);
86783 for (i = 0; i < turns.length; i++) {
86784 var turn = turns[i];
86785 if (seen[turn.restrictionID]) continue; // avoid deleting the turn twice (#4968, #4928)
86787 if (turn.direct && turn.path[1] === datum.path[1]) {
86788 seen[turns[i].restrictionID] = true;
86789 turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86791 _oldTurns.push(turn);
86793 extraActions.push(actionUnrestrictTurn(turn));
86797 actions = _intersection.actions.concat(extraActions, [actionRestrictTurn(datumOnly, restrictionType), _t('operations.restriction.annotation.create')]);
86798 } else if (datum.restrictionID) {
86800 // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86801 // This relies on the assumption that the intersection was already split up when we
86802 // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86803 turns = _oldTurns || [];
86806 for (i = 0; i < turns.length; i++) {
86807 if (turns[i].key !== datum.key) {
86808 extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86813 actions = _intersection.actions.concat(extraActions, [actionUnrestrictTurn(datum), _t('operations.restriction.annotation.delete')]);
86816 actions = _intersection.actions.concat([actionRestrictTurn(datum, restrictionType), _t('operations.restriction.annotation.create')]);
86819 context.perform.apply(context, actions); // At this point the datum will be changed, but will have same key..
86820 // Refresh it and update the help..
86822 var s = surface.selectAll('.' + datum.key);
86823 datum = s.empty() ? null : s.datum();
86824 updateHints(datum);
86832 function mouseover(d3_event) {
86833 var datum = d3_event.target.__data__;
86834 updateHints(datum);
86837 _lastXPos = _lastXPos || sdims[0];
86839 function redraw(minChange) {
86843 xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
86846 if (!minChange || minChange && Math.abs(xPos - _lastXPos) >= minChange) {
86847 if (context.hasEntity(_vertexID)) {
86850 _container.call(renderViewer);
86855 function highlightPathsFrom(wayID) {
86856 surface.selectAll('.related').classed('related', false).classed('allow', false).classed('restrict', false).classed('only', false);
86857 surface.selectAll('.' + wayID).classed('related', true);
86860 var turns = _intersection.turns(wayID, _maxViaWay);
86862 for (var i = 0; i < turns.length; i++) {
86863 var turn = turns[i];
86864 var ids = [turn.to.way];
86865 var klass = turn.no ? 'restrict' : turn.only ? 'only' : 'allow';
86867 if (turn.only || turns.length === 1) {
86868 if (turn.via.ways) {
86869 ids = ids.concat(turn.via.ways);
86871 } else if (turn.to.way === wayID) {
86875 surface.selectAll(utilEntitySelector(ids)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only');
86880 function updateHints(datum) {
86881 var help = _container.selectAll('.restriction-help').html('');
86883 var placeholders = {};
86884 ['from', 'via', 'to'].forEach(function (k) {
86885 placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
86887 var entity = datum && datum.properties && datum.properties.entity;
86894 way = vgraph.entity(_fromWayID);
86895 surface.selectAll('.' + _fromWayID).classed('selected', true).classed('related', true);
86896 } // Hovering a way
86899 if (datum instanceof osmWay && datum.__from) {
86901 highlightPathsFrom(_fromWayID ? null : way.id);
86902 surface.selectAll('.' + way.id).classed('related', true);
86903 var clickSelect = !_fromWayID || _fromWayID !== way.id;
86904 help.append('div') // "Click to select FROM {fromName}." / "FROM {fromName}"
86905 .html(_t.html('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
86906 from: placeholders.from,
86907 fromName: displayName(way.id, vgraph)
86908 })); // Hovering a turn arrow
86909 } else if (datum instanceof osmTurn) {
86910 var restrictionType = osmInferRestriction(vgraph, datum, projection);
86911 var turnType = restrictionType.replace(/^(only|no)\_/, '');
86912 var indirect = datum.direct === false ? _t.html('restriction.help.indirect') : '';
86913 var klass, turnText, nextText;
86916 klass = 'restrict';
86917 turnText = _t.html('restriction.help.turn.no_' + turnType, {
86920 nextText = _t.html('restriction.help.turn.only_' + turnType, {
86923 } else if (datum.only) {
86925 turnText = _t.html('restriction.help.turn.only_' + turnType, {
86928 nextText = _t.html('restriction.help.turn.allowed_' + turnType, {
86933 turnText = _t.html('restriction.help.turn.allowed_' + turnType, {
86936 nextText = _t.html('restriction.help.turn.no_' + turnType, {
86941 help.append('div') // "NO Right Turn (indirect)"
86942 .attr('class', 'qualifier ' + klass).html(turnText);
86943 help.append('div') // "FROM {fromName} TO {toName}"
86944 .html(_t.html('restriction.help.from_name_to_name', {
86945 from: placeholders.from,
86946 fromName: displayName(datum.from.way, vgraph),
86947 to: placeholders.to,
86948 toName: displayName(datum.to.way, vgraph)
86951 if (datum.via.ways && datum.via.ways.length) {
86954 for (var i = 0; i < datum.via.ways.length; i++) {
86955 var prev = names[names.length - 1];
86956 var curr = displayName(datum.via.ways[i], vgraph);
86957 if (!prev || curr !== prev) // collapse identical names
86961 help.append('div') // "VIA {viaNames}"
86962 .html(_t.html('restriction.help.via_names', {
86963 via: placeholders.via,
86964 viaNames: names.join(', ')
86969 help.append('div') // Click for "No Right Turn"
86970 .html(_t.html('restriction.help.toggle', {
86971 turn: nextText.trim()
86975 highlightPathsFrom(null);
86976 var alongIDs = datum.path.slice();
86977 surface.selectAll(utilEntitySelector(alongIDs)).classed('related', true).classed('allow', klass === 'allow').classed('restrict', klass === 'restrict').classed('only', klass === 'only'); // Hovering empty surface
86979 highlightPathsFrom(null);
86982 help.append('div') // "FROM {fromName}"
86983 .html(_t.html('restriction.help.from_name', {
86984 from: placeholders.from,
86985 fromName: displayName(_fromWayID, vgraph)
86988 help.append('div') // "Click to select a FROM segment."
86989 .html(_t.html('restriction.help.select_from', {
86990 from: placeholders.from
86997 function displayMaxDistance(maxDist) {
86998 var isImperial = !_mainLocalizer.usesMetric();
87003 // imprecise conversion for prettier display
87013 distance: _t('units.feet', {
87014 quantity: distToFeet
87019 distance: _t('units.meters', {
87025 return _t.html('restriction.controls.distance_up_to', opts);
87028 function displayMaxVia(maxVia) {
87029 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');
87032 function displayName(entityID, graph) {
87033 var entity = graph.entity(entityID);
87034 var name = utilDisplayName(entity) || '';
87035 var matched = _mainPresetIndex.match(entity, graph);
87036 var type = matched && matched.name() || utilDisplayType(entity.id);
87037 return name || type;
87040 restrictions.entityIDs = function (val) {
87041 _intersection = null;
87044 _vertexID = val[0];
87047 restrictions.tags = function () {};
87049 restrictions.focus = function () {};
87051 restrictions.off = function (selection) {
87052 if (!_initialized) return;
87053 selection.selectAll('.surface').call(breathe.off).on('click.restrictions', null).on('mouseover.restrictions', null);
87054 select(window).on('resize.restrictions', null);
87057 return utilRebind(restrictions, dispatch$1, 'on');
87059 uiFieldRestrictions.supportsMultiselection = false;
87061 function uiFieldTextarea(field, context) {
87062 var dispatch$1 = dispatch('change');
87063 var input = select(null);
87067 function textarea(selection) {
87068 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
87069 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
87070 input = wrap.selectAll('textarea').data([0]);
87071 input = input.enter().append('textarea').attr('id', field.domId).call(utilNoAuto).on('input', change(true)).on('blur', change()).on('change', change()).merge(input);
87074 function change(onInput) {
87075 return function () {
87076 var val = utilGetSetValue(input);
87077 if (!onInput) val = context.cleanTagValue(val); // don't override multiple values with blank string
87079 if (!val && Array.isArray(_tags[field.key])) return;
87081 t[field.key] = val || undefined;
87082 dispatch$1.call('change', this, t, onInput);
87086 textarea.tags = function (tags) {
87088 var isMixed = Array.isArray(tags[field.key]);
87089 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);
87092 textarea.focus = function () {
87093 input.node().focus();
87096 return utilRebind(textarea, dispatch$1, 'on');
87099 function uiFieldWikidata(field, context) {
87100 var wikidata = services.wikidata;
87101 var dispatch$1 = dispatch('change');
87103 var _selection = select(null);
87105 var _searchInput = select(null);
87108 var _wikidataEntity = null;
87110 var _entityIDs = [];
87112 var _wikipediaKey = field.keys && field.keys.find(function (key) {
87113 return key.includes('wikipedia');
87115 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
87117 var combobox = uiCombobox(context, 'combo-' + field.safeid).caseSensitive(true).minItems(1);
87119 function wiki(selection) {
87120 _selection = selection;
87121 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
87122 wrap = wrap.enter().append('div').attr('class', 'form-field-input-wrap form-field-input-' + field.type).merge(wrap);
87123 var list = wrap.selectAll('ul').data([0]);
87124 list = list.enter().append('ul').attr('class', 'rows').merge(list);
87125 var searchRow = list.selectAll('li.wikidata-search').data([0]);
87126 var searchRowEnter = searchRow.enter().append('li').attr('class', 'wikidata-search');
87127 searchRowEnter.append('input').attr('type', 'text').attr('id', field.domId).style('flex', '1').call(utilNoAuto).on('focus', function () {
87128 var node = select(this).node();
87129 node.setSelectionRange(0, node.value.length);
87130 }).on('blur', function () {
87131 setLabelForEntity();
87132 }).call(combobox.fetcher(fetchWikidataItems));
87133 combobox.on('accept', function (d) {
87138 }).on('cancel', function () {
87139 setLabelForEntity();
87141 searchRowEnter.append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
87142 domain: 'wikidata.org'
87143 })).call(svgIcon('#iD-icon-out-link')).on('click', function (d3_event) {
87144 d3_event.preventDefault();
87145 if (_wikiURL) window.open(_wikiURL, '_blank');
87147 searchRow = searchRow.merge(searchRowEnter);
87148 _searchInput = searchRow.select('input');
87149 var wikidataProperties = ['description', 'identifier'];
87150 var items = list.selectAll('li.labeled-input').data(wikidataProperties); // Enter
87152 var enter = items.enter().append('li').attr('class', function (d) {
87153 return 'labeled-input preset-wikidata-' + d;
87155 enter.append('span').attr('class', 'label').html(function (d) {
87156 return _t.html('wikidata.' + d);
87158 enter.append('input').attr('type', 'text').call(utilNoAuto).classed('disabled', 'true').attr('readonly', 'true');
87159 enter.append('button').attr('class', 'form-field-button').attr('title', _t('icons.copy')).call(svgIcon('#iD-operation-copy')).on('click', function (d3_event) {
87160 d3_event.preventDefault();
87161 select(this.parentNode).select('input').node().select();
87162 document.execCommand('copy');
87166 function fetchWikidataItems(q, callback) {
87167 if (!q && _hintKey) {
87168 // other tags may be good search terms
87169 for (var i in _entityIDs) {
87170 var entity = context.hasEntity(_entityIDs[i]);
87172 if (entity.tags[_hintKey]) {
87173 q = entity.tags[_hintKey];
87179 wikidata.itemsForSearchQuery(q, function (err, data) {
87182 for (var i in data) {
87183 data[i].value = data[i].label + ' (' + data[i].id + ')';
87184 data[i].title = data[i].description;
87187 if (callback) callback(data);
87191 function change() {
87193 syncTags[field.key] = _qid;
87194 dispatch$1.call('change', this, syncTags); // attempt asynchronous update of wikidata tag..
87196 var initGraph = context.graph();
87197 var initEntityIDs = _entityIDs;
87198 wikidata.entityByQID(_qid, function (err, entity) {
87199 if (err) return; // If graph has changed, we can't apply this update.
87201 if (context.graph() !== initGraph) return;
87202 if (!entity.sitelinks) return;
87203 var langs = wikidata.languagesToQuery(); // use the label and description languages as fallbacks
87205 ['labels', 'descriptions'].forEach(function (key) {
87206 if (!entity[key]) return;
87207 var valueLangs = Object.keys(entity[key]);
87208 if (valueLangs.length === 0) return;
87209 var valueLang = valueLangs[0];
87211 if (langs.indexOf(valueLang) === -1) {
87212 langs.push(valueLang);
87215 var newWikipediaValue;
87217 if (_wikipediaKey) {
87218 var foundPreferred;
87220 for (var i in langs) {
87221 var lang = langs[i];
87222 var siteID = lang.replace('-', '_') + 'wiki';
87224 if (entity.sitelinks[siteID]) {
87225 foundPreferred = true;
87226 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title; // use the first match
87232 if (!foundPreferred) {
87233 // No wikipedia sites available in the user's language or the fallback languages,
87234 // default to any wikipedia sitelink
87235 var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function (site) {
87236 return site.endsWith('wiki');
87239 if (wikiSiteKeys.length === 0) {
87240 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87241 newWikipediaValue = null;
87243 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87244 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87245 newWikipediaValue = wikiLang + ':' + wikiTitle;
87250 if (newWikipediaValue) {
87251 newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87254 if (typeof newWikipediaValue === 'undefined') return;
87255 var actions = initEntityIDs.map(function (entityID) {
87256 var entity = context.hasEntity(entityID);
87257 if (!entity) return null;
87258 var currTags = Object.assign({}, entity.tags); // shallow copy
87260 if (newWikipediaValue === null) {
87261 if (!currTags[_wikipediaKey]) return null;
87262 delete currTags[_wikipediaKey];
87264 currTags[_wikipediaKey] = newWikipediaValue;
87267 return actionChangeTags(entityID, currTags);
87268 }).filter(Boolean);
87269 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
87271 context.overwrite(function actionUpdateWikipediaTags(graph) {
87272 actions.forEach(function (action) {
87273 graph = action(graph);
87276 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
87277 // changeTags() is not intended to be called asynchronously
87281 function setLabelForEntity() {
87284 if (_wikidataEntity) {
87285 label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87287 if (label.length === 0) {
87288 label = _wikidataEntity.id.toString();
87292 utilGetSetValue(_searchInput, label);
87295 wiki.tags = function (tags) {
87296 var isMixed = Array.isArray(tags[field.key]);
87298 _searchInput.attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null).attr('placeholder', isMixed ? _t('inspector.multiple_values') : '').classed('mixed', isMixed);
87300 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87302 if (!/^Q[0-9]*$/.test(_qid)) {
87303 // not a proper QID
87306 } // QID value in correct format
87309 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87310 wikidata.entityByQID(_qid, function (err, entity) {
87316 _wikidataEntity = entity;
87317 setLabelForEntity();
87318 var description = entityPropertyForDisplay(entity, 'descriptions');
87320 _selection.select('button.wiki-link').classed('disabled', false);
87322 _selection.select('.preset-wikidata-description').style('display', function () {
87323 return description.length > 0 ? 'flex' : 'none';
87324 }).select('input').attr('value', description);
87326 _selection.select('.preset-wikidata-identifier').style('display', function () {
87327 return entity.id ? 'flex' : 'none';
87328 }).select('input').attr('value', entity.id);
87329 }); // not a proper QID
87331 function unrecognized() {
87332 _wikidataEntity = null;
87333 setLabelForEntity();
87335 _selection.select('.preset-wikidata-description').style('display', 'none');
87337 _selection.select('.preset-wikidata-identifier').style('display', 'none');
87339 _selection.select('button.wiki-link').classed('disabled', true);
87341 if (_qid && _qid !== '') {
87342 _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87349 function entityPropertyForDisplay(wikidataEntity, propKey) {
87350 if (!wikidataEntity[propKey]) return '';
87351 var propObj = wikidataEntity[propKey];
87352 var langKeys = Object.keys(propObj);
87353 if (langKeys.length === 0) return ''; // sorted by priority, since we want to show the user's language first if possible
87355 var langs = wikidata.languagesToQuery();
87357 for (var i in langs) {
87358 var lang = langs[i];
87359 var valueObj = propObj[lang];
87360 if (valueObj && valueObj.value && valueObj.value.length > 0) return valueObj.value;
87361 } // default to any available value
87364 return propObj[langKeys[0]].value;
87367 wiki.entityIDs = function (val) {
87368 if (!arguments.length) return _entityIDs;
87373 wiki.focus = function () {
87374 _searchInput.node().focus();
87377 return utilRebind(wiki, dispatch$1, 'on');
87380 function uiFieldWikipedia(field, context) {
87381 var _arguments = arguments;
87382 var dispatch$1 = dispatch('change');
87383 var wikipedia = services.wikipedia;
87384 var wikidata = services.wikidata;
87386 var _langInput = select(null);
87388 var _titleInput = select(null);
87396 var _dataWikipedia = [];
87397 _mainFileFetcher.get('wmf_sitematrix').then(function (d) {
87398 _dataWikipedia = d;
87399 if (_tags) updateForTags(_tags);
87400 })["catch"](function () {
87403 var langCombo = uiCombobox(context, 'wikipedia-lang').fetcher(function (value, callback) {
87404 var v = value.toLowerCase();
87405 callback(_dataWikipedia.filter(function (d) {
87406 return d[0].toLowerCase().indexOf(v) >= 0 || d[1].toLowerCase().indexOf(v) >= 0 || d[2].toLowerCase().indexOf(v) >= 0;
87407 }).map(function (d) {
87413 var titleCombo = uiCombobox(context, 'wikipedia-title').fetcher(function (value, callback) {
87417 for (var i in _entityIDs) {
87418 var entity = context.hasEntity(_entityIDs[i]);
87420 if (entity.tags.name) {
87421 value = entity.tags.name;
87427 var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87428 searchfn(language()[2], value, function (query, data) {
87429 callback(data.map(function (d) {
87437 function wiki(selection) {
87438 var wrap = selection.selectAll('.form-field-input-wrap').data([0]);
87439 wrap = wrap.enter().append('div').attr('class', "form-field-input-wrap form-field-input-".concat(field.type)).merge(wrap);
87440 var langContainer = wrap.selectAll('.wiki-lang-container').data([0]);
87441 langContainer = langContainer.enter().append('div').attr('class', 'wiki-lang-container').merge(langContainer);
87442 _langInput = langContainer.selectAll('input.wiki-lang').data([0]);
87443 _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);
87445 _langInput.on('blur', changeLang).on('change', changeLang);
87447 var titleContainer = wrap.selectAll('.wiki-title-container').data([0]);
87448 titleContainer = titleContainer.enter().append('div').attr('class', 'wiki-title-container').merge(titleContainer);
87449 _titleInput = titleContainer.selectAll('input.wiki-title').data([0]);
87450 _titleInput = _titleInput.enter().append('input').attr('type', 'text').attr('class', 'wiki-title').attr('id', field.domId).call(utilNoAuto).call(titleCombo).merge(_titleInput);
87452 _titleInput.on('blur', function () {
87454 }).on('change', function () {
87458 var link = titleContainer.selectAll('.wiki-link').data([0]);
87459 link = link.enter().append('button').attr('class', 'form-field-button wiki-link').attr('title', _t('icons.view_on', {
87460 domain: 'wikipedia.org'
87461 })).call(svgIcon('#iD-icon-out-link')).merge(link);
87462 link.on('click', function (d3_event) {
87463 d3_event.preventDefault();
87464 if (_wikiURL) window.open(_wikiURL, '_blank');
87468 function defaultLanguageInfo(skipEnglishFallback) {
87469 var langCode = _mainLocalizer.languageCode().toLowerCase();
87471 for (var i in _dataWikipedia) {
87472 var d = _dataWikipedia[i]; // default to the language of iD's current locale
87474 if (d[2] === langCode) return d;
87475 } // fallback to English
87478 return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
87481 function language(skipEnglishFallback) {
87482 var value = utilGetSetValue(_langInput).toLowerCase();
87484 for (var i in _dataWikipedia) {
87485 var d = _dataWikipedia[i]; // return the language already set in the UI, if supported
87487 if (d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value) return d;
87488 } // fallback to English
87491 return defaultLanguageInfo(skipEnglishFallback);
87494 function changeLang() {
87495 utilGetSetValue(_langInput, language()[1]);
87499 function change(skipWikidata) {
87500 var value = utilGetSetValue(_titleInput);
87501 var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87503 var langInfo = m && _dataWikipedia.find(function (d) {
87504 return m[1] === d[2];
87510 var nativeLangName = langInfo[1]; // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87512 value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87515 var anchor; // try {
87516 // leave this out for now - #6232
87517 // Best-effort `anchordecode:` implementation
87518 // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87521 anchor = decodeURIComponent(m[3]); // }
87523 value += '#' + anchor.replace(/_/g, ' ');
87526 value = value.slice(0, 1).toUpperCase() + value.slice(1);
87527 utilGetSetValue(_langInput, nativeLangName);
87528 utilGetSetValue(_titleInput, value);
87532 syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87534 syncTags.wikipedia = undefined;
87537 dispatch$1.call('change', this, syncTags);
87538 if (skipWikidata || !value || !language()[2]) return; // attempt asynchronous update of wikidata tag..
87540 var initGraph = context.graph();
87541 var initEntityIDs = _entityIDs;
87542 wikidata.itemsByTitle(language()[2], value, function (err, data) {
87543 if (err || !data || !Object.keys(data).length) return; // If graph has changed, we can't apply this update.
87545 if (context.graph() !== initGraph) return;
87546 var qids = Object.keys(data);
87547 var value = qids && qids.find(function (id) {
87548 return id.match(/^Q\d+$/);
87550 var actions = initEntityIDs.map(function (entityID) {
87551 var entity = context.entity(entityID).tags;
87552 var currTags = Object.assign({}, entity); // shallow copy
87554 if (currTags.wikidata !== value) {
87555 currTags.wikidata = value;
87556 return actionChangeTags(entityID, currTags);
87560 }).filter(Boolean);
87561 if (!actions.length) return; // Coalesce the update of wikidata tag into the previous tag change
87563 context.overwrite(function actionUpdateWikidataTags(graph) {
87564 actions.forEach(function (action) {
87565 graph = action(graph);
87568 }, context.history().undoAnnotation()); // do not dispatch.call('change') here, because entity_editor
87569 // changeTags() is not intended to be called asynchronously
87573 wiki.tags = function (tags) {
87575 updateForTags(tags);
87578 function updateForTags(tags) {
87579 var value = typeof tags[field.key] === 'string' ? tags[field.key] : ''; // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
87580 // optional suffix of `#anchor`
87582 var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87583 var tagLang = m && m[1];
87584 var tagArticleTitle = m && m[2];
87585 var anchor = m && m[3];
87587 var tagLangInfo = tagLang && _dataWikipedia.find(function (d) {
87588 return tagLang === d[2];
87589 }); // value in correct format
87593 var nativeLangName = tagLangInfo[1];
87594 utilGetSetValue(_langInput, nativeLangName);
87595 utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? '#' + anchor : ''));
87599 // Best-effort `anchorencode:` implementation
87600 anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87602 anchor = anchor.replace(/ /g, '_');
87606 _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' + tagArticleTitle.replace(/ /g, '_') + (anchor ? '#' + anchor : ''); // unrecognized value format
87608 utilGetSetValue(_titleInput, value);
87610 if (value && value !== '') {
87611 utilGetSetValue(_langInput, '');
87612 var defaultLangInfo = defaultLanguageInfo();
87613 _wikiURL = "https://".concat(defaultLangInfo[2], ".wikipedia.org/w/index.php?fulltext=1&search=").concat(value);
87615 var shownOrDefaultLangInfo = language(true
87616 /* skipEnglishFallback */
87618 utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
87624 wiki.entityIDs = function (val) {
87625 if (!_arguments.length) return _entityIDs;
87630 wiki.focus = function () {
87631 _titleInput.node().focus();
87634 return utilRebind(wiki, dispatch$1, 'on');
87636 uiFieldWikipedia.supportsMultiselection = false;
87639 access: uiFieldAccess,
87640 address: uiFieldAddress,
87641 check: uiFieldCheck,
87642 combo: uiFieldCombo,
87643 cycleway: uiFieldCycleway,
87644 defaultCheck: uiFieldCheck,
87645 email: uiFieldText,
87646 identifier: uiFieldText,
87647 lanes: uiFieldLanes,
87648 localized: uiFieldLocalized,
87649 maxspeed: uiFieldMaxspeed,
87650 manyCombo: uiFieldCombo,
87651 multiCombo: uiFieldCombo,
87652 networkCombo: uiFieldCombo,
87653 number: uiFieldText,
87654 onewayCheck: uiFieldCheck,
87655 radio: uiFieldRadio,
87656 restrictions: uiFieldRestrictions,
87657 semiCombo: uiFieldCombo,
87658 structureRadio: uiFieldRadio,
87661 textarea: uiFieldTextarea,
87662 typeCombo: uiFieldCombo,
87664 wikidata: uiFieldWikidata,
87665 wikipedia: uiFieldWikipedia
87668 function uiField(context, presetField, entityIDs, options) {
87669 options = Object.assign({
87676 var dispatch$1 = dispatch('change', 'revert');
87677 var field = Object.assign({}, presetField); // shallow copy
87679 field.domId = utilUniqueDomId('form-field-' + field.safeid);
87680 var _show = options.show;
87683 var _locked = false;
87685 var _lockedTip = uiTooltip().title(_t.html('inspector.lock.suggestion', {
87687 })).placement('bottom');
87689 field.keys = field.keys || [field.key]; // only create the fields that are actually being shown
87691 if (_show && !field.impl) {
87693 } // Creates the field.. This is done lazily,
87694 // once we know that the field will be shown.
87697 function createField() {
87698 field.impl = uiFields[field.type](field, context).on('change', function (t, onInput) {
87699 dispatch$1.call('change', field, t, onInput);
87703 field.entityIDs = entityIDs; // if this field cares about the entities, pass them along
87705 if (field.impl.entityIDs) {
87706 field.impl.entityIDs(entityIDs);
87711 function isModified() {
87712 if (!entityIDs || !entityIDs.length) return false;
87713 return entityIDs.some(function (entityID) {
87714 var original = context.graph().base().entities[entityID];
87715 var latest = context.graph().entity(entityID);
87716 return field.keys.some(function (key) {
87717 return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
87722 function tagsContainFieldKey() {
87723 return field.keys.some(function (key) {
87724 if (field.type === 'multiCombo') {
87725 for (var tagKey in _tags) {
87726 if (tagKey.indexOf(key) === 0) {
87734 return _tags[key] !== undefined;
87738 function revert(d3_event, d) {
87739 d3_event.stopPropagation();
87740 d3_event.preventDefault();
87741 if (!entityIDs || _locked) return;
87742 dispatch$1.call('revert', d, d.keys);
87745 function remove(d3_event, d) {
87746 d3_event.stopPropagation();
87747 d3_event.preventDefault();
87748 if (_locked) return;
87750 d.keys.forEach(function (key) {
87751 t[key] = undefined;
87753 dispatch$1.call('change', d, t);
87756 field.render = function (selection) {
87757 var container = selection.selectAll('.form-field').data([field]); // Enter
87759 var enter = container.enter().append('div').attr('class', function (d) {
87760 return 'form-field form-field-' + d.safeid;
87761 }).classed('nowrap', !options.wrap);
87763 if (options.wrap) {
87764 var labelEnter = enter.append('label').attr('class', 'field-label').attr('for', function (d) {
87767 var textEnter = labelEnter.append('span').attr('class', 'label-text');
87768 textEnter.append('span').attr('class', 'label-textvalue').html(function (d) {
87771 textEnter.append('span').attr('class', 'label-textannotation');
87773 if (options.remove) {
87774 labelEnter.append('button').attr('class', 'remove-icon').attr('title', _t('icons.remove')).call(svgIcon('#iD-operation-delete'));
87777 if (options.revert) {
87778 labelEnter.append('button').attr('class', 'modified-icon').attr('title', _t('icons.undo')).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo'));
87783 container = container.merge(enter);
87784 container.select('.field-label > .remove-icon') // propagate bound data
87785 .on('click', remove);
87786 container.select('.field-label > .modified-icon') // propagate bound data
87787 .on('click', revert);
87788 container.each(function (d) {
87789 var selection = select(this);
87795 var reference, help; // instantiate field help
87797 if (options.wrap && field.type === 'restrictions') {
87798 help = uiFieldHelp(context, 'restrictions');
87799 } // instantiate tag reference
87802 if (options.wrap && options.info) {
87803 var referenceKey = d.key || '';
87805 if (d.type === 'multiCombo') {
87806 // lookup key without the trailing ':'
87807 referenceKey = referenceKey.replace(/:$/, '');
87810 reference = uiTagReference(d.reference || {
87814 if (_state === 'hover') {
87815 reference.showing(false);
87819 selection.call(d.impl); // add field help components
87822 selection.call(help.body).select('.field-label').call(help.button);
87823 } // add tag reference components
87827 selection.call(reference.body).select('.field-label').call(reference.button);
87830 d.impl.tags(_tags);
87832 container.classed('locked', _locked).classed('modified', isModified()).classed('present', tagsContainFieldKey()); // show a tip and lock icon if the field is locked
87834 var annotation = container.selectAll('.field-label .label-textannotation');
87835 var icon = annotation.selectAll('.icon').data(_locked ? [0] : []);
87836 icon.exit().remove();
87837 icon.enter().append('svg').attr('class', 'icon').append('use').attr('xlink:href', '#fas-lock');
87838 container.call(_locked ? _lockedTip : _lockedTip.destroy);
87841 field.state = function (val) {
87842 if (!arguments.length) return _state;
87847 field.tags = function (val) {
87848 if (!arguments.length) return _tags;
87851 if (tagsContainFieldKey() && !_show) {
87852 // always show a field if it has a value to display
87863 field.locked = function (val) {
87864 if (!arguments.length) return _locked;
87869 field.show = function () {
87876 if (field["default"] && field.key && _tags[field.key] !== field["default"]) {
87878 t[field.key] = field["default"];
87879 dispatch$1.call('change', this, t);
87881 }; // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
87884 field.isShown = function () {
87886 }; // An allowed field can appear in the UI or in the 'Add field' dropdown.
87887 // A non-allowed field is hidden from the user altogether
87890 field.isAllowed = function () {
87891 if (entityIDs && entityIDs.length > 1 && uiFields[field.type].supportsMultiselection === false) return false;
87892 if (field.geometry && !entityIDs.every(function (entityID) {
87893 return field.matchGeometry(context.graph().geometry(entityID));
87896 if (field.countryCodes || field.notCountryCodes) {
87897 var extent = combinedEntityExtent();
87898 if (!extent) return true;
87899 var center = extent.center();
87900 var countryCode = iso1A2Code(center);
87901 if (!countryCode) return false;
87902 countryCode = countryCode.toLowerCase();
87904 if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
87908 if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
87913 var prerequisiteTag = field.prerequisiteTag;
87915 if (entityIDs && !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
87917 if (!entityIDs.every(function (entityID) {
87918 var entity = context.graph().entity(entityID);
87920 if (prerequisiteTag.key) {
87921 var value = entity.tags[prerequisiteTag.key];
87922 if (!value) return false;
87924 if (prerequisiteTag.valueNot) {
87925 return prerequisiteTag.valueNot !== value;
87928 if (prerequisiteTag.value) {
87929 return prerequisiteTag.value === value;
87931 } else if (prerequisiteTag.keyNot) {
87932 if (entity.tags[prerequisiteTag.keyNot]) return false;
87942 field.focus = function () {
87944 field.impl.focus();
87948 function combinedEntityExtent() {
87949 return entityIDs && entityIDs.length && entityIDs.reduce(function (extent, entityID) {
87950 var entity = context.graph().entity(entityID);
87951 return extent.extend(entity.extent(context.graph()));
87955 return utilRebind(field, dispatch$1, 'on');
87958 function uiFormFields(context) {
87959 var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
87960 var _fieldsArr = [];
87961 var _lastPlaceholder = '';
87965 function formFields(selection) {
87966 var allowedFields = _fieldsArr.filter(function (field) {
87967 return field.isAllowed();
87970 var shown = allowedFields.filter(function (field) {
87971 return field.isShown();
87973 var notShown = allowedFields.filter(function (field) {
87974 return !field.isShown();
87976 var container = selection.selectAll('.form-fields-container').data([0]);
87977 container = container.enter().append('div').attr('class', 'form-fields-container ' + (_klass || '')).merge(container);
87978 var fields = container.selectAll('.wrap-form-field').data(shown, function (d) {
87979 return d.id + (d.entityIDs ? d.entityIDs.join() : '');
87981 fields.exit().remove(); // Enter
87983 var enter = fields.enter().append('div').attr('class', function (d) {
87984 return 'wrap-form-field wrap-form-field-' + d.safeid;
87987 fields = fields.merge(enter);
87988 fields.order().each(function (d) {
87989 select(this).call(d.render);
87992 var moreFields = notShown.map(function (field) {
87993 var title = field.title();
87994 titles.push(title);
87995 var terms = field.terms();
87996 if (field.key) terms.push(field.key);
87997 if (field.keys) terms = terms.concat(field.keys);
87999 display: field.label(),
88006 var placeholder = titles.slice(0, 3).join(', ') + (titles.length > 3 ? '…' : '');
88007 var more = selection.selectAll('.more-fields').data(_state === 'hover' || moreFields.length === 0 ? [] : [0]);
88008 more.exit().remove();
88009 var moreEnter = more.enter().append('div').attr('class', 'more-fields').append('label');
88010 moreEnter.append('span').html(_t.html('inspector.add_fields'));
88011 more = moreEnter.merge(more);
88012 var input = more.selectAll('.value').data([0]);
88013 input.exit().remove();
88014 input = input.enter().append('input').attr('class', 'value').attr('type', 'text').attr('placeholder', placeholder).call(utilNoAuto).merge(input);
88015 input.call(utilGetSetValue, '').call(moreCombo.data(moreFields).on('accept', function (d) {
88016 if (!d) return; // user entered something that was not matched
88018 var field = d.field;
88020 selection.call(formFields); // rerender
88023 })); // avoid updating placeholder excessively (triggers style recalc)
88025 if (_lastPlaceholder !== placeholder) {
88026 input.attr('placeholder', placeholder);
88027 _lastPlaceholder = placeholder;
88031 formFields.fieldsArr = function (val) {
88032 if (!arguments.length) return _fieldsArr;
88033 _fieldsArr = val || [];
88037 formFields.state = function (val) {
88038 if (!arguments.length) return _state;
88043 formFields.klass = function (val) {
88044 if (!arguments.length) return _klass;
88052 function uiSectionPresetFields(context) {
88053 var section = uiSection('preset-fields', context).label(_t.html('inspector.fields')).disclosureContent(renderDisclosureContent);
88054 var dispatch$1 = dispatch('change', 'revert');
88055 var formFields = uiFormFields(context);
88067 function renderDisclosureContent(selection) {
88069 var graph = context.graph();
88070 var geometries = Object.keys(_entityIDs.reduce(function (geoms, entityID) {
88071 geoms[graph.entity(entityID).geometry(graph)] = true;
88074 var presetsManager = _mainPresetIndex;
88075 var allFields = [];
88076 var allMoreFields = [];
88077 var sharedTotalFields;
88079 _presets.forEach(function (preset) {
88080 var fields = preset.fields();
88081 var moreFields = preset.moreFields();
88082 allFields = utilArrayUnion(allFields, fields);
88083 allMoreFields = utilArrayUnion(allMoreFields, moreFields);
88085 if (!sharedTotalFields) {
88086 sharedTotalFields = utilArrayUnion(fields, moreFields);
88088 sharedTotalFields = sharedTotalFields.filter(function (field) {
88089 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
88094 var sharedFields = allFields.filter(function (field) {
88095 return sharedTotalFields.indexOf(field) !== -1;
88097 var sharedMoreFields = allMoreFields.filter(function (field) {
88098 return sharedTotalFields.indexOf(field) !== -1;
88101 sharedFields.forEach(function (field) {
88102 if (field.matchAllGeometry(geometries)) {
88103 _fieldsArr.push(uiField(context, field, _entityIDs));
88106 var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
88108 if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
88109 _fieldsArr.push(uiField(context, presetsManager.field('restrictions'), _entityIDs));
88112 var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
88113 additionalFields.sort(function (field1, field2) {
88114 return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
88116 additionalFields.forEach(function (field) {
88117 if (sharedFields.indexOf(field) === -1 && field.matchAllGeometry(geometries)) {
88118 _fieldsArr.push(uiField(context, field, _entityIDs, {
88124 _fieldsArr.forEach(function (field) {
88125 field.on('change', function (t, onInput) {
88126 dispatch$1.call('change', field, _entityIDs, t, onInput);
88127 }).on('revert', function (keys) {
88128 dispatch$1.call('revert', field, keys);
88133 _fieldsArr.forEach(function (field) {
88134 field.state(_state).tags(_tags);
88137 selection.call(formFields.fieldsArr(_fieldsArr).state(_state).klass('grouped-items-area'));
88138 selection.selectAll('.wrap-form-field input').on('keydown', function (d3_event) {
88139 // if user presses enter, and combobox is not active, accept edits..
88140 if (d3_event.keyCode === 13 && // ↩ Return
88141 context.container().select('.combobox').empty()) {
88142 context.enter(modeBrowse(context));
88147 section.presets = function (val) {
88148 if (!arguments.length) return _presets;
88150 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88158 section.state = function (val) {
88159 if (!arguments.length) return _state;
88164 section.tags = function (val) {
88165 if (!arguments.length) return _tags;
88166 _tags = val; // Don't reset _fieldsArr here.
88171 section.entityIDs = function (val) {
88172 if (!arguments.length) return _entityIDs;
88174 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88182 return utilRebind(section, dispatch$1, 'on');
88185 function uiSectionRawMemberEditor(context) {
88186 var section = uiSection('raw-member-editor', context).shouldDisplay(function () {
88187 if (!_entityIDs || _entityIDs.length !== 1) return false;
88188 var entity = context.hasEntity(_entityIDs[0]);
88189 return entity && entity.type === 'relation';
88190 }).label(function () {
88191 var entity = context.hasEntity(_entityIDs[0]);
88192 if (!entity) return '';
88193 var gt = entity.members.length > _maxMembers ? '>' : '';
88194 var count = gt + entity.members.slice(0, _maxMembers).length;
88195 return _t('inspector.title_count', {
88196 title: _t.html('inspector.members'),
88199 }).disclosureContent(renderDisclosureContent);
88200 var taginfo = services.taginfo;
88204 var _maxMembers = 1000;
88206 function downloadMember(d3_event, d) {
88207 d3_event.preventDefault(); // display the loading indicator
88209 select(this.parentNode).classed('tag-reference-loading', true);
88210 context.loadEntity(d.id, function () {
88211 section.reRender();
88215 function zoomToMember(d3_event, d) {
88216 d3_event.preventDefault();
88217 var entity = context.entity(d.id);
88218 context.map().zoomToEase(entity); // highlight the feature in case it wasn't previously on-screen
88220 utilHighlightEntities([d.id], true, context);
88223 function selectMember(d3_event, d) {
88224 d3_event.preventDefault(); // remove the hover-highlight styling
88226 utilHighlightEntities([d.id], false, context);
88227 var entity = context.entity(d.id);
88228 var mapExtent = context.map().extent();
88230 if (!entity.intersects(mapExtent, context.graph())) {
88231 // zoom to the entity if its extent is not visible now
88232 context.map().zoomToEase(entity);
88235 context.enter(modeSelect(context, [d.id]));
88238 function changeRole(d3_event, d) {
88239 var oldRole = d.role;
88240 var newRole = context.cleanRelationRole(select(this).property('value'));
88242 if (oldRole !== newRole) {
88248 context.perform(actionChangeMember(d.relation.id, member, d.index), _t('operations.change_role.annotation', {
88251 context.validator().validate();
88255 function deleteMember(d3_event, d) {
88256 // remove the hover-highlight styling
88257 utilHighlightEntities([d.id], false, context);
88258 context.perform(actionDeleteMember(d.relation.id, d.index), _t('operations.delete_member.annotation', {
88262 if (!context.hasEntity(d.relation.id)) {
88263 // Removing the last member will also delete the relation.
88264 // If this happens we need to exit the selection mode
88265 context.enter(modeBrowse(context));
88267 // Changing the mode also runs `validate`, but otherwise we need to
88268 // rerun it manually
88269 context.validator().validate();
88273 function renderDisclosureContent(selection) {
88274 var entityID = _entityIDs[0];
88275 var memberships = [];
88276 var entity = context.entity(entityID);
88277 entity.members.slice(0, _maxMembers).forEach(function (member, index) {
88284 member: context.hasEntity(member.id),
88285 domId: utilUniqueDomId(entityID + '-member-' + index)
88288 var list = selection.selectAll('.member-list').data([0]);
88289 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
88290 var items = list.selectAll('li').data(memberships, function (d) {
88291 return osmEntity.key(d.relation) + ',' + d.index + ',' + (d.member ? osmEntity.key(d.member) : 'incomplete');
88293 items.exit().each(unbind).remove();
88294 var itemsEnter = items.enter().append('li').attr('class', 'member-row form-field').classed('member-incomplete', function (d) {
88297 itemsEnter.each(function (d) {
88298 var item = select(this);
88299 var label = item.append('label').attr('class', 'field-label').attr('for', d.domId);
88302 // highlight the member feature in the map while hovering on the list item
88303 item.on('mouseover', function () {
88304 utilHighlightEntities([d.id], true, context);
88305 }).on('mouseout', function () {
88306 utilHighlightEntities([d.id], false, context);
88308 var labelLink = label.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectMember);
88309 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
88310 var matched = _mainPresetIndex.match(d.member, context.graph());
88311 return matched && matched.name() || utilDisplayType(d.member.id);
88313 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
88314 return utilDisplayName(d.member);
88316 label.append('button').attr('title', _t('icons.remove')).attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete'));
88317 label.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToMember);
88319 var labelText = label.append('span').attr('class', 'label-text');
88320 labelText.append('span').attr('class', 'member-entity-type').html(_t.html('inspector.' + d.type, {
88323 labelText.append('span').attr('class', 'member-entity-name').html(_t.html('inspector.incomplete', {
88326 label.append('button').attr('class', 'member-download').attr('title', _t('icons.download')).call(svgIcon('#iD-icon-load')).on('click', downloadMember);
88329 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88330 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
88332 }).property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto);
88335 wrapEnter.each(bindTypeahead);
88339 items = items.merge(itemsEnter).order();
88340 items.select('input.member-role').property('value', function (d) {
88342 }).on('blur', changeRole).on('change', changeRole);
88343 items.select('button.member-delete').on('click', deleteMember);
88344 var dragOrigin, targetIndex;
88345 items.call(d3_drag().on('start', function (d3_event) {
88350 targetIndex = null;
88351 }).on('drag', function (d3_event) {
88352 var x = d3_event.x - dragOrigin.x,
88353 y = d3_event.y - dragOrigin.y;
88354 if (!select(this).classed('dragging') && // don't display drag until dragging beyond a distance threshold
88355 Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
88356 var index = items.nodes().indexOf(this);
88357 select(this).classed('dragging', true);
88358 targetIndex = null;
88359 selection.selectAll('li.member-row').style('transform', function (d2, index2) {
88360 var node = select(this).node();
88362 if (index === index2) {
88363 return 'translate(' + x + 'px, ' + y + 'px)';
88364 } else if (index2 > index && d3_event.y > node.offsetTop) {
88365 if (targetIndex === null || index2 > targetIndex) {
88366 targetIndex = index2;
88369 return 'translateY(-100%)';
88370 } else if (index2 < index && d3_event.y < node.offsetTop + node.offsetHeight) {
88371 if (targetIndex === null || index2 < targetIndex) {
88372 targetIndex = index2;
88375 return 'translateY(100%)';
88380 }).on('end', function (d3_event, d) {
88381 if (!select(this).classed('dragging')) return;
88382 var index = items.nodes().indexOf(this);
88383 select(this).classed('dragging', false);
88384 selection.selectAll('li.member-row').style('transform', null);
88386 if (targetIndex !== null) {
88387 // dragged to a new position, reorder
88388 context.perform(actionMoveMember(d.relation.id, index, targetIndex), _t('operations.reorder_members.annotation'));
88389 context.validator().validate();
88393 function bindTypeahead(d) {
88394 var row = select(this);
88395 var role = row.selectAll('input.member-role');
88396 var origValue = role.property('value');
88398 function sort(value, data) {
88399 var sameletter = [];
88402 for (var i = 0; i < data.length; i++) {
88403 if (data[i].value.substring(0, value.length) === value) {
88404 sameletter.push(data[i]);
88406 other.push(data[i]);
88410 return sameletter.concat(other);
88413 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
88414 // The `geometry` param is used in the `taginfo.js` interface for
88415 // filtering results, as a key into the `tag_members_fractions`
88416 // object. If we don't know the geometry because the member is
88417 // not yet downloaded, it's ok to guess based on type.
88421 geometry = context.graph().geometry(d.member.id);
88422 } else if (d.type === 'relation') {
88423 geometry = 'relation';
88424 } else if (d.type === 'way') {
88427 geometry = 'point';
88430 var rtype = entity.tags.type;
88433 rtype: rtype || '',
88434 geometry: geometry,
88436 }, function (err, data) {
88437 if (!err) callback(sort(role, data));
88439 }).on('cancel', function () {
88440 role.property('value', origValue);
88444 function unbind() {
88445 var row = select(this);
88446 row.selectAll('input.member-role').call(uiCombobox.off, context);
88450 section.entityIDs = function (val) {
88451 if (!arguments.length) return _entityIDs;
88459 function actionDeleteMembers(relationId, memberIndexes) {
88460 return function (graph) {
88461 // Remove the members in descending order so removals won't shift what members
88462 // are at the remaining indexes
88463 memberIndexes.sort(function (a, b) {
88467 for (var i in memberIndexes) {
88468 graph = actionDeleteMember(relationId, memberIndexes[i])(graph);
88475 function uiSectionRawMembershipEditor(context) {
88476 var section = uiSection('raw-membership-editor', context).shouldDisplay(function () {
88477 return _entityIDs && _entityIDs.length;
88478 }).label(function () {
88479 var parents = getSharedParentRelations();
88480 var gt = parents.length > _maxMemberships ? '>' : '';
88481 var count = gt + parents.slice(0, _maxMemberships).length;
88482 return _t('inspector.title_count', {
88483 title: _t.html('inspector.relations'),
88486 }).disclosureContent(renderDisclosureContent);
88487 var taginfo = services.taginfo;
88488 var nearbyCombo = uiCombobox(context, 'parent-relation').minItems(1).fetcher(fetchNearbyRelations).itemsMouseEnter(function (d3_event, d) {
88489 if (d.relation) utilHighlightEntities([d.relation.id], true, context);
88490 }).itemsMouseLeave(function (d3_event, d) {
88491 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
88493 var _inChange = false;
88494 var _entityIDs = [];
88498 var _maxMemberships = 1000;
88500 function getSharedParentRelations() {
88503 for (var i = 0; i < _entityIDs.length; i++) {
88504 var entity = context.graph().hasEntity(_entityIDs[i]);
88505 if (!entity) continue;
88508 parents = context.graph().parentRelations(entity);
88510 parents = utilArrayIntersection(parents, context.graph().parentRelations(entity));
88513 if (!parents.length) break;
88519 function getMemberships() {
88520 var memberships = [];
88521 var relations = getSharedParentRelations().slice(0, _maxMemberships);
88522 var isMultiselect = _entityIDs.length > 1;
88523 var i, relation, membership, index, member, indexedMember;
88525 for (i = 0; i < relations.length; i++) {
88526 relation = relations[i];
88528 relation: relation,
88530 hash: osmEntity.key(relation)
88533 for (index = 0; index < relation.members.length; index++) {
88534 member = relation.members[index];
88536 if (_entityIDs.indexOf(member.id) !== -1) {
88537 indexedMember = Object.assign({}, member, {
88540 membership.members.push(indexedMember);
88541 membership.hash += ',' + index.toString();
88543 if (!isMultiselect) {
88544 // For single selections, list one entry per membership per relation.
88545 // For multiselections, list one entry per relation.
88546 memberships.push(membership);
88548 relation: relation,
88550 hash: osmEntity.key(relation)
88556 if (membership.members.length) memberships.push(membership);
88559 memberships.forEach(function (membership) {
88560 membership.domId = utilUniqueDomId('membership-' + membership.relation.id);
88562 membership.members.forEach(function (member) {
88563 if (roles.indexOf(member.role) === -1) roles.push(member.role);
88565 membership.role = roles.length === 1 ? roles[0] : roles;
88567 return memberships;
88570 function selectRelation(d3_event, d) {
88571 d3_event.preventDefault(); // remove the hover-highlight styling
88573 utilHighlightEntities([d.relation.id], false, context);
88574 context.enter(modeSelect(context, [d.relation.id]));
88577 function zoomToRelation(d3_event, d) {
88578 d3_event.preventDefault();
88579 var entity = context.entity(d.relation.id);
88580 context.map().zoomToEase(entity); // highlight the relation in case it wasn't previously on-screen
88582 utilHighlightEntities([d.relation.id], true, context);
88585 function changeRole(d3_event, d) {
88586 if (d === 0) return; // called on newrow (shouldn't happen)
88588 if (_inChange) return; // avoid accidental recursive call #5731
88590 var newRole = context.cleanRelationRole(select(this).property('value'));
88591 if (!newRole.trim() && typeof d.role !== 'string') return;
88592 var membersToUpdate = d.members.filter(function (member) {
88593 return member.role !== newRole;
88596 if (membersToUpdate.length) {
88598 context.perform(function actionChangeMemberRoles(graph) {
88599 membersToUpdate.forEach(function (member) {
88600 var newMember = Object.assign({}, member, {
88603 delete newMember.index;
88604 graph = actionChangeMember(d.relation.id, newMember, member.index)(graph);
88607 }, _t('operations.change_role.annotation', {
88608 n: membersToUpdate.length
88610 context.validator().validate();
88616 function addMembership(d, role) {
88617 this.blur(); // avoid keeping focus on the button
88619 _showBlank = false;
88621 function actionAddMembers(relationId, ids, role) {
88622 return function (graph) {
88623 for (var i in ids) {
88626 type: graph.entity(ids[i]).type,
88629 graph = actionAddMember(relationId, member)(graph);
88637 context.perform(actionAddMembers(d.relation.id, _entityIDs, role), _t('operations.add_member.annotation', {
88638 n: _entityIDs.length
88640 context.validator().validate();
88642 var relation = osmRelation();
88643 context.perform(actionAddEntity(relation), actionAddMembers(relation.id, _entityIDs, role), _t('operations.add.annotation.relation')); // changing the mode also runs `validate`
88645 context.enter(modeSelect(context, [relation.id]).newFeature(true));
88649 function deleteMembership(d3_event, d) {
88650 this.blur(); // avoid keeping focus on the button
88652 if (d === 0) return; // called on newrow (shouldn't happen)
88653 // remove the hover-highlight styling
88655 utilHighlightEntities([d.relation.id], false, context);
88656 var indexes = d.members.map(function (member) {
88657 return member.index;
88659 context.perform(actionDeleteMembers(d.relation.id, indexes), _t('operations.delete_member.annotation', {
88660 n: _entityIDs.length
88662 context.validator().validate();
88665 function fetchNearbyRelations(q, callback) {
88666 var newRelation = {
88668 value: _t('inspector.new_relation'),
88669 display: _t.html('inspector.new_relation')
88671 var entityID = _entityIDs[0];
88673 var graph = context.graph();
88675 function baseDisplayLabel(entity) {
88676 var matched = _mainPresetIndex.match(entity, graph);
88677 var presetName = matched && matched.name() || _t('inspector.relation');
88678 var entityName = utilDisplayName(entity) || '';
88679 return presetName + ' ' + entityName;
88682 var explicitRelation = q && context.hasEntity(q.toLowerCase());
88684 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
88685 // loaded relation is specified explicitly, only show that
88687 relation: explicitRelation,
88688 value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
88691 context.history().intersects(context.map().extent()).forEach(function (entity) {
88692 if (entity.type !== 'relation' || entity.id === entityID) return;
88693 var value = baseDisplayLabel(entity);
88694 if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return;
88700 result.sort(function (a, b) {
88701 return osmRelation.creationOrder(a.relation, b.relation);
88702 }); // Dedupe identical names by appending relation id - see #2891
88704 var dupeGroups = Object.values(utilArrayGroupBy(result, 'value')).filter(function (v) {
88705 return v.length > 1;
88707 dupeGroups.forEach(function (group) {
88708 group.forEach(function (obj) {
88709 obj.value += ' ' + obj.relation.id;
88714 result.forEach(function (obj) {
88715 obj.title = obj.value;
88717 result.unshift(newRelation);
88721 function renderDisclosureContent(selection) {
88722 var memberships = getMemberships();
88723 var list = selection.selectAll('.member-list').data([0]);
88724 list = list.enter().append('ul').attr('class', 'member-list').merge(list);
88725 var items = list.selectAll('li.member-row-normal').data(memberships, function (d) {
88728 items.exit().each(unbind).remove(); // Enter
88730 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
88732 itemsEnter.on('mouseover', function (d3_event, d) {
88733 utilHighlightEntities([d.relation.id], true, context);
88734 }).on('mouseout', function (d3_event, d) {
88735 utilHighlightEntities([d.relation.id], false, context);
88737 var labelEnter = itemsEnter.append('label').attr('class', 'field-label').attr('for', function (d) {
88740 var labelLink = labelEnter.append('span').attr('class', 'label-text').append('a').attr('href', '#').on('click', selectRelation);
88741 labelLink.append('span').attr('class', 'member-entity-type').html(function (d) {
88742 var matched = _mainPresetIndex.match(d.relation, context.graph());
88743 return matched && matched.name() || _t('inspector.relation');
88745 labelLink.append('span').attr('class', 'member-entity-name').html(function (d) {
88746 return utilDisplayName(d.relation);
88748 labelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', deleteMembership);
88749 labelEnter.append('button').attr('class', 'member-zoom').attr('title', _t('icons.zoom_to')).call(svgIcon('#iD-icon-framed-dot', 'monochrome')).on('click', zoomToRelation);
88750 var wrapEnter = itemsEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88751 wrapEnter.append('input').attr('class', 'member-role').attr('id', function (d) {
88753 }).property('type', 'text').property('value', function (d) {
88754 return typeof d.role === 'string' ? d.role : '';
88755 }).attr('title', function (d) {
88756 return Array.isArray(d.role) ? d.role.filter(Boolean).join('\n') : d.role;
88757 }).attr('placeholder', function (d) {
88758 return Array.isArray(d.role) ? _t('inspector.multiple_roles') : _t('inspector.role');
88759 }).classed('mixed', function (d) {
88760 return Array.isArray(d.role);
88761 }).call(utilNoAuto).on('blur', changeRole).on('change', changeRole);
88764 wrapEnter.each(bindTypeahead);
88767 var newMembership = list.selectAll('.member-row-new').data(_showBlank ? [0] : []); // Exit
88769 newMembership.exit().remove(); // Enter
88771 var newMembershipEnter = newMembership.enter().append('li').attr('class', 'member-row member-row-new form-field');
88772 var newLabelEnter = newMembershipEnter.append('label').attr('class', 'field-label');
88773 newLabelEnter.append('input').attr('placeholder', _t('inspector.choose_relation')).attr('type', 'text').attr('class', 'member-entity-input').call(utilNoAuto);
88774 newLabelEnter.append('button').attr('class', 'remove member-delete').call(svgIcon('#iD-operation-delete')).on('click', function () {
88775 list.selectAll('.member-row-new').remove();
88777 var newWrapEnter = newMembershipEnter.append('div').attr('class', 'form-field-input-wrap form-field-input-member');
88778 newWrapEnter.append('input').attr('class', 'member-role').property('type', 'text').attr('placeholder', _t('inspector.role')).call(utilNoAuto); // Update
88780 newMembership = newMembership.merge(newMembershipEnter);
88781 newMembership.selectAll('.member-entity-input').on('blur', cancelEntity) // if it wasn't accepted normally, cancel it
88782 .call(nearbyCombo.on('accept', acceptEntity).on('cancel', cancelEntity)); // Container for the Add button
88784 var addRow = selection.selectAll('.add-row').data([0]); // enter
88786 var addRowEnter = addRow.enter().append('div').attr('class', 'add-row');
88787 var addRelationButton = addRowEnter.append('button').attr('class', 'add-relation');
88788 addRelationButton.call(svgIcon('#iD-icon-plus', 'light'));
88789 addRelationButton.call(uiTooltip().title(_t.html('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
88790 addRowEnter.append('div').attr('class', 'space-value'); // preserve space
88792 addRowEnter.append('div').attr('class', 'space-buttons'); // preserve space
88795 addRow = addRow.merge(addRowEnter);
88796 addRow.select('.add-relation').on('click', function () {
88798 section.reRender();
88799 list.selectAll('.member-entity-input').node().focus();
88802 function acceptEntity(d) {
88806 } // remove hover-higlighting
88809 if (d.relation) utilHighlightEntities([d.relation.id], false, context);
88810 var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
88811 addMembership(d, role);
88814 function cancelEntity() {
88815 var input = newMembership.selectAll('.member-entity-input');
88816 input.property('value', ''); // remove hover-higlighting
88818 context.surface().selectAll('.highlighted').classed('highlighted', false);
88821 function bindTypeahead(d) {
88822 var row = select(this);
88823 var role = row.selectAll('input.member-role');
88824 var origValue = role.property('value');
88826 function sort(value, data) {
88827 var sameletter = [];
88830 for (var i = 0; i < data.length; i++) {
88831 if (data[i].value.substring(0, value.length) === value) {
88832 sameletter.push(data[i]);
88834 other.push(data[i]);
88838 return sameletter.concat(other);
88841 role.call(uiCombobox(context, 'member-role').fetcher(function (role, callback) {
88842 var rtype = d.relation.tags.type;
88845 rtype: rtype || '',
88846 geometry: context.graph().geometry(_entityIDs[0]),
88848 }, function (err, data) {
88849 if (!err) callback(sort(role, data));
88851 }).on('cancel', function () {
88852 role.property('value', origValue);
88856 function unbind() {
88857 var row = select(this);
88858 row.selectAll('input.member-role').call(uiCombobox.off, context);
88862 section.entityIDs = function (val) {
88863 if (!arguments.length) return _entityIDs;
88865 _showBlank = false;
88872 function uiSectionSelectionList(context) {
88873 var _selectedIDs = [];
88874 var section = uiSection('selected-features', context).shouldDisplay(function () {
88875 return _selectedIDs.length > 1;
88876 }).label(function () {
88877 return _t('inspector.title_count', {
88878 title: _t.html('inspector.features'),
88879 count: _selectedIDs.length
88881 }).disclosureContent(renderDisclosureContent);
88882 context.history().on('change.selectionList', function (difference) {
88884 section.reRender();
88888 section.entityIDs = function (val) {
88889 if (!arguments.length) return _selectedIDs;
88890 _selectedIDs = val;
88894 function selectEntity(d3_event, entity) {
88895 context.enter(modeSelect(context, [entity.id]));
88898 function deselectEntity(d3_event, entity) {
88899 var selectedIDs = _selectedIDs.slice();
88901 var index = selectedIDs.indexOf(entity.id);
88904 selectedIDs.splice(index, 1);
88905 context.enter(modeSelect(context, selectedIDs));
88909 function renderDisclosureContent(selection) {
88910 var list = selection.selectAll('.feature-list').data([0]);
88911 list = list.enter().append('ul').attr('class', 'feature-list').merge(list);
88913 var entities = _selectedIDs.map(function (id) {
88914 return context.hasEntity(id);
88915 }).filter(Boolean);
88917 var items = list.selectAll('.feature-list-item').data(entities, osmEntity.key);
88918 items.exit().remove(); // Enter
88920 var enter = items.enter().append('li').attr('class', 'feature-list-item').each(function (d) {
88921 select(this).on('mouseover', function () {
88922 utilHighlightEntities([d.id], true, context);
88923 }).on('mouseout', function () {
88924 utilHighlightEntities([d.id], false, context);
88927 var label = enter.append('button').attr('class', 'label').on('click', selectEntity);
88928 label.append('span').attr('class', 'entity-geom-icon').call(svgIcon('', 'pre-text'));
88929 label.append('span').attr('class', 'entity-type');
88930 label.append('span').attr('class', 'entity-name');
88931 enter.append('button').attr('class', 'close').attr('title', _t('icons.deselect')).on('click', deselectEntity).call(svgIcon('#iD-icon-close')); // Update
88933 items = items.merge(enter);
88934 items.selectAll('.entity-geom-icon use').attr('href', function () {
88935 var entity = this.parentNode.parentNode.__data__;
88936 return '#iD-icon-' + entity.geometry(context.graph());
88938 items.selectAll('.entity-type').html(function (entity) {
88939 return _mainPresetIndex.match(entity, context.graph()).name();
88941 items.selectAll('.entity-name').html(function (d) {
88942 // fetch latest entity
88943 var entity = context.entity(d.id);
88944 return utilDisplayName(entity);
88951 function uiEntityEditor(context) {
88952 var dispatch$1 = dispatch('choose');
88953 var _state = 'select';
88954 var _coalesceChanges = false;
88955 var _modified = false;
88961 var _activePresets = [];
88967 function entityEditor(selection) {
88968 var combinedTags = utilCombinedTags(_entityIDs, context.graph()); // Header
88970 var header = selection.selectAll('.header').data([0]); // Enter
88972 var headerEnter = header.enter().append('div').attr('class', 'header fillL');
88973 headerEnter.append('button').attr('class', 'preset-reset preset-choose').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-forward' : '#iD-icon-backward'));
88974 headerEnter.append('button').attr('class', 'close').on('click', function () {
88975 context.enter(modeBrowse(context));
88976 }).call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
88977 headerEnter.append('h3'); // Update
88979 header = header.merge(headerEnter);
88980 header.selectAll('h3').html(_entityIDs.length === 1 ? _t.html('inspector.edit') : _t.html('inspector.edit_features'));
88981 header.selectAll('.preset-reset').on('click', function () {
88982 dispatch$1.call('choose', this, _activePresets);
88985 var body = selection.selectAll('.inspector-body').data([0]); // Enter
88987 var bodyEnter = body.enter().append('div').attr('class', 'entity-editor inspector-body sep-top'); // Update
88989 body = body.merge(bodyEnter);
88992 _sections = [uiSectionSelectionList(context), uiSectionFeatureType(context).on('choose', function (presets) {
88993 dispatch$1.call('choose', this, presets);
88994 }), uiSectionEntityIssues(context), uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags), uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags), uiSectionRawMemberEditor(context), uiSectionRawMembershipEditor(context)];
88997 _sections.forEach(function (section) {
88998 if (section.entityIDs) {
88999 section.entityIDs(_entityIDs);
89002 if (section.presets) {
89003 section.presets(_activePresets);
89006 if (section.tags) {
89007 section.tags(combinedTags);
89010 if (section.state) {
89011 section.state(_state);
89014 body.call(section.render);
89017 context.history().on('change.entity-editor', historyChanged);
89019 function historyChanged(difference) {
89020 if (selection.selectAll('.entity-editor').empty()) return;
89021 if (_state === 'hide') return;
89022 var significant = !difference || difference.didChange.properties || difference.didChange.addition || difference.didChange.deletion;
89023 if (!significant) return;
89024 _entityIDs = _entityIDs.filter(context.hasEntity);
89025 if (!_entityIDs.length) return;
89026 var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
89027 loadActivePresets();
89028 var graph = context.graph();
89029 entityEditor.modified(_base !== graph);
89030 entityEditor(selection);
89032 if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
89033 // flash the button to indicate the preset changed
89034 context.container().selectAll('.entity-editor button.preset-reset .label').style('background-color', '#fff').transition().duration(750).style('background-color', null);
89037 } // Tag changes that fire on input can all get coalesced into a single
89038 // history operation when the user leaves the field. #2342
89039 // Use explicit entityIDs in case the selection changes before the event is fired.
89042 function changeTags(entityIDs, changed, onInput) {
89045 for (var i in entityIDs) {
89046 var entityID = entityIDs[i];
89047 var entity = context.entity(entityID);
89048 var tags = Object.assign({}, entity.tags); // shallow copy
89050 for (var k in changed) {
89052 var v = changed[k];
89054 if (v !== undefined || tags.hasOwnProperty(k)) {
89060 tags = utilCleanTags(tags);
89063 if (!fastDeepEqual(entity.tags, tags)) {
89064 actions.push(actionChangeTags(entityID, tags));
89068 if (actions.length) {
89069 var combinedAction = function combinedAction(graph) {
89070 actions.forEach(function (action) {
89071 graph = action(graph);
89076 var annotation = _t('operations.change_tags.annotation');
89078 if (_coalesceChanges) {
89079 context.overwrite(combinedAction, annotation);
89081 context.perform(combinedAction, annotation);
89082 _coalesceChanges = !!onInput;
89084 } // if leaving field (blur event), rerun validation
89088 context.validator().validate();
89092 function revertTags(keys) {
89095 for (var i in _entityIDs) {
89096 var entityID = _entityIDs[i];
89097 var original = context.graph().base().entities[entityID];
89100 for (var j in keys) {
89102 changed[key] = original ? original.tags[key] : undefined;
89105 var entity = context.entity(entityID);
89106 var tags = Object.assign({}, entity.tags); // shallow copy
89108 for (var k in changed) {
89110 var v = changed[k];
89112 if (v !== undefined || tags.hasOwnProperty(k)) {
89117 tags = utilCleanTags(tags);
89119 if (!fastDeepEqual(entity.tags, tags)) {
89120 actions.push(actionChangeTags(entityID, tags));
89124 if (actions.length) {
89125 var combinedAction = function combinedAction(graph) {
89126 actions.forEach(function (action) {
89127 graph = action(graph);
89132 var annotation = _t('operations.change_tags.annotation');
89134 if (_coalesceChanges) {
89135 context.overwrite(combinedAction, annotation);
89137 context.perform(combinedAction, annotation);
89138 _coalesceChanges = false;
89142 context.validator().validate();
89145 entityEditor.modified = function (val) {
89146 if (!arguments.length) return _modified;
89148 return entityEditor;
89151 entityEditor.state = function (val) {
89152 if (!arguments.length) return _state;
89154 return entityEditor;
89157 entityEditor.entityIDs = function (val) {
89158 if (!arguments.length) return _entityIDs;
89159 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) return entityEditor; // exit early if no change
89162 _base = context.graph();
89163 _coalesceChanges = false;
89164 loadActivePresets(true);
89165 return entityEditor.modified(false);
89168 entityEditor.newFeature = function (val) {
89169 if (!arguments.length) return _newFeature;
89171 return entityEditor;
89174 function loadActivePresets(isForNewSelection) {
89175 var graph = context.graph();
89178 for (var i in _entityIDs) {
89179 var entity = graph.hasEntity(_entityIDs[i]);
89180 if (!entity) return;
89181 var match = _mainPresetIndex.match(entity, graph);
89182 if (!counts[match.id]) counts[match.id] = 0;
89183 counts[match.id] += 1;
89186 var matches = Object.keys(counts).sort(function (p1, p2) {
89187 return counts[p2] - counts[p1];
89188 }).map(function (pID) {
89189 return _mainPresetIndex.item(pID);
89192 if (!isForNewSelection) {
89193 // A "weak" preset doesn't set any tags. (e.g. "Address")
89194 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")
89196 if (weakPreset && matches.length === 1 && matches[0].isFallback()) return;
89199 entityEditor.presets(matches);
89202 entityEditor.presets = function (val) {
89203 if (!arguments.length) return _activePresets; // don't reload the same preset
89205 if (!utilArrayIdentical(val, _activePresets)) {
89206 _activePresets = val;
89209 return entityEditor;
89212 return utilRebind(entityEditor, dispatch$1, 'on');
89215 function uiPresetList(context) {
89216 var dispatch$1 = dispatch('cancel', 'choose');
89220 var _currentPresets;
89222 var _autofocus = false;
89224 function presetList(selection) {
89225 if (!_entityIDs) return;
89226 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
89227 selection.html('');
89228 var messagewrap = selection.append('div').attr('class', 'header fillL');
89229 var message = messagewrap.append('h3').html(_t.html('inspector.choose'));
89230 messagewrap.append('button').attr('class', 'preset-choose').on('click', function () {
89231 dispatch$1.call('cancel', this);
89232 }).call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'));
89234 function initialKeydown(d3_event) {
89235 // hack to let delete shortcut work when search is autofocused
89236 if (search.property('value').length === 0 && (d3_event.keyCode === utilKeybinding.keyCodes['⌫'] || d3_event.keyCode === utilKeybinding.keyCodes['⌦'])) {
89237 d3_event.preventDefault();
89238 d3_event.stopPropagation();
89239 operationDelete(context, _entityIDs)(); // hack to let undo work when search is autofocused
89240 } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === utilKeybinding.keyCodes.z) {
89241 d3_event.preventDefault();
89242 d3_event.stopPropagation();
89244 } else if (!d3_event.ctrlKey && !d3_event.metaKey) {
89245 // don't check for delete/undo hack on future keydown events
89246 select(this).on('keydown', keydown);
89247 keydown.call(this, d3_event);
89251 function keydown(d3_event) {
89253 if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] && // if insertion point is at the end of the string
89254 search.node().selectionStart === search.property('value').length) {
89255 d3_event.preventDefault();
89256 d3_event.stopPropagation(); // move focus to the first item in the preset list
89258 var buttons = list.selectAll('.preset-list-button');
89259 if (!buttons.empty()) buttons.nodes()[0].focus();
89263 function keypress(d3_event) {
89265 var value = search.property('value');
89267 if (d3_event.keyCode === 13 && // ↩ Return
89269 list.selectAll('.preset-list-item:first-child').each(function (d) {
89270 d.choose.call(this);
89275 function inputevent() {
89276 var value = search.property('value');
89277 list.classed('filtered', value.length);
89278 var extent = combinedEntityExtent();
89279 var results, messageText;
89281 if (value.length && extent) {
89282 var center = extent.center();
89283 var countryCode = iso1A2Code(center);
89284 results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
89285 messageText = _t('inspector.results', {
89286 n: results.collection.length,
89290 results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
89291 messageText = _t('inspector.choose');
89294 list.call(drawList, results);
89295 message.html(messageText);
89298 var searchWrap = selection.append('div').attr('class', 'search-header');
89299 searchWrap.call(svgIcon('#iD-icon-search', 'pre-text'));
89300 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);
89303 search.node().focus(); // Safari 14 doesn't always like to focus immediately,
89304 // so try again on the next pass
89306 setTimeout(function () {
89307 search.node().focus();
89311 var listWrap = selection.append('div').attr('class', 'inspector-body');
89312 var list = listWrap.append('div').attr('class', 'preset-list').call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
89313 context.features().on('change.preset-list', updateForFeatureHiddenState);
89316 function drawList(list, presets) {
89317 presets = presets.matchAllGeometry(entityGeometries());
89318 var collection = presets.collection.reduce(function (collection, preset) {
89319 if (!preset) return collection;
89321 if (preset.members) {
89322 if (preset.members.collection.filter(function (preset) {
89323 return preset.addable();
89325 collection.push(CategoryItem(preset));
89327 } else if (preset.addable()) {
89328 collection.push(PresetItem(preset));
89333 var items = list.selectAll('.preset-list-item').data(collection, function (d) {
89334 return d.preset.id;
89337 items.exit().remove();
89338 items.enter().append('div').attr('class', function (item) {
89339 return 'preset-list-item preset-' + item.preset.id.replace('/', '-');
89340 }).classed('current', function (item) {
89341 return _currentPresets.indexOf(item.preset) !== -1;
89342 }).each(function (item) {
89343 select(this).call(item);
89344 }).style('opacity', 0).transition().style('opacity', 1);
89345 updateForFeatureHiddenState();
89348 function itemKeydown(d3_event) {
89349 // the actively focused item
89350 var item = select(this.closest('.preset-list-item'));
89351 var parentItem = select(item.node().parentNode.closest('.preset-list-item')); // arrow down, move focus to the next, lower item
89353 if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
89354 d3_event.preventDefault();
89355 d3_event.stopPropagation(); // the next item in the list at the same level
89357 var nextItem = select(item.node().nextElementSibling); // if there is no next item in this list
89359 if (nextItem.empty()) {
89360 // if there is a parent item
89361 if (!parentItem.empty()) {
89362 // the item is the last item of a sublist,
89363 // select the next item at the parent level
89364 nextItem = select(parentItem.node().nextElementSibling);
89365 } // if the focused item is expanded
89367 } else if (select(this).classed('expanded')) {
89368 // select the first subitem instead
89369 nextItem = item.select('.subgrid .preset-list-item:first-child');
89372 if (!nextItem.empty()) {
89373 // focus on the next item
89374 nextItem.select('.preset-list-button').node().focus();
89375 } // arrow up, move focus to the previous, higher item
89377 } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
89378 d3_event.preventDefault();
89379 d3_event.stopPropagation(); // the previous item in the list at the same level
89381 var previousItem = select(item.node().previousElementSibling); // if there is no previous item in this list
89383 if (previousItem.empty()) {
89384 // if there is a parent item
89385 if (!parentItem.empty()) {
89386 // the item is the first subitem of a sublist select the parent item
89387 previousItem = parentItem;
89388 } // if the previous item is expanded
89390 } else if (previousItem.select('.preset-list-button').classed('expanded')) {
89391 // select the last subitem of the sublist of the previous item
89392 previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
89395 if (!previousItem.empty()) {
89396 // focus on the previous item
89397 previousItem.select('.preset-list-button').node().focus();
89399 // the focus is at the top of the list, move focus back to the search field
89400 var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
89401 search.node().focus();
89402 } // arrow left, move focus to the parent item if there is one
89404 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
89405 d3_event.preventDefault();
89406 d3_event.stopPropagation(); // if there is a parent item, focus on the parent item
89408 if (!parentItem.empty()) {
89409 parentItem.select('.preset-list-button').node().focus();
89410 } // arrow right, choose this item
89412 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
89413 d3_event.preventDefault();
89414 d3_event.stopPropagation();
89415 item.datum().choose.call(select(this).node());
89419 function CategoryItem(preset) {
89424 function item(selection) {
89425 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap category');
89428 var isExpanded = select(this).classed('expanded');
89429 var iconName = isExpanded ? _mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward' : '#iD-icon-down';
89430 select(this).classed('expanded', !isExpanded);
89431 select(this).selectAll('div.label-inner svg.icon use').attr('href', iconName);
89435 var geometries = entityGeometries();
89436 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) {
89437 // right arrow, expand the focused item
89438 if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '←' : '→']) {
89439 d3_event.preventDefault();
89440 d3_event.stopPropagation(); // if the item isn't expanded
89442 if (!select(this).classed('expanded')) {
89443 // toggle expansion (expand the item)
89444 click.call(this, d3_event);
89445 } // left arrow, collapse the focused item
89447 } else if (d3_event.keyCode === utilKeybinding.keyCodes[_mainLocalizer.textDirection() === 'rtl' ? '→' : '←']) {
89448 d3_event.preventDefault();
89449 d3_event.stopPropagation(); // if the item is expanded
89451 if (select(this).classed('expanded')) {
89452 // toggle expansion (collapse the item)
89453 click.call(this, d3_event);
89456 itemKeydown.call(this, d3_event);
89459 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
89460 label.append('div').attr('class', 'namepart').call(svgIcon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward', 'inline')).append('span').html(function () {
89461 return preset.nameLabel() + '…';
89463 box = selection.append('div').attr('class', 'subgrid').style('max-height', '0px').style('opacity', 0);
89464 box.append('div').attr('class', 'arrow');
89465 sublist = box.append('div').attr('class', 'preset-list fillL3');
89468 item.choose = function () {
89469 if (!box || !sublist) return;
89473 box.transition().duration(200).style('opacity', '0').style('max-height', '0px').style('padding-bottom', '0px');
89476 var members = preset.members.matchAllGeometry(entityGeometries());
89477 sublist.call(drawList, members);
89478 box.transition().duration(200).style('opacity', '1').style('max-height', 200 + members.collection.length * 190 + 'px').style('padding-bottom', '10px');
89482 item.preset = preset;
89486 function PresetItem(preset) {
89487 function item(selection) {
89488 var wrap = selection.append('div').attr('class', 'preset-list-button-wrap');
89489 var geometries = entityGeometries();
89490 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);
89491 var label = button.append('div').attr('class', 'label').append('div').attr('class', 'label-inner');
89492 var nameparts = [preset.nameLabel(), preset.subtitleLabel()].filter(Boolean);
89493 label.selectAll('.namepart').data(nameparts).enter().append('div').attr('class', 'namepart').html(function (d) {
89496 wrap.call(item.reference.button);
89497 selection.call(item.reference.body);
89500 item.choose = function () {
89501 if (select(this).classed('disabled')) return;
89503 if (!context.inIntro()) {
89504 _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
89507 context.perform(function (graph) {
89508 for (var i in _entityIDs) {
89509 var entityID = _entityIDs[i];
89510 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
89511 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
89515 }, _t('operations.change_tags.annotation'));
89516 context.validator().validate(); // rerun validation
89518 dispatch$1.call('choose', this, preset);
89521 item.help = function (d3_event) {
89522 d3_event.stopPropagation();
89523 item.reference.toggle();
89526 item.preset = preset;
89527 item.reference = uiTagReference(preset.reference());
89531 function updateForFeatureHiddenState() {
89532 if (!_entityIDs.every(context.hasEntity)) return;
89533 var geometries = entityGeometries();
89534 var button = context.container().selectAll('.preset-list .preset-list-button'); // remove existing tooltips
89536 button.call(uiTooltip().destroyAny);
89537 button.each(function (item, index) {
89538 var hiddenPresetFeaturesId;
89540 for (var i in geometries) {
89541 hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
89542 if (hiddenPresetFeaturesId) break;
89545 var isHiddenPreset = !context.inIntro() && !!hiddenPresetFeaturesId && (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
89546 select(this).classed('disabled', isHiddenPreset);
89548 if (isHiddenPreset) {
89549 var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
89550 select(this).call(uiTooltip().title(_t.html('inspector.hidden_preset.' + (isAutoHidden ? 'zoom' : 'manual'), {
89551 features: _t.html('feature.' + hiddenPresetFeaturesId + '.description')
89552 })).placement(index < 2 ? 'bottom' : 'top'));
89557 presetList.autofocus = function (val) {
89558 if (!arguments.length) return _autofocus;
89563 presetList.entityIDs = function (val) {
89564 if (!arguments.length) return _entityIDs;
89567 if (_entityIDs && _entityIDs.length) {
89568 var presets = _entityIDs.map(function (entityID) {
89569 return _mainPresetIndex.match(context.entity(entityID), context.graph());
89572 presetList.presets(presets);
89578 presetList.presets = function (val) {
89579 if (!arguments.length) return _currentPresets;
89580 _currentPresets = val;
89584 function entityGeometries() {
89587 for (var i in _entityIDs) {
89588 var entityID = _entityIDs[i];
89589 var entity = context.entity(entityID);
89590 var geometry = entity.geometry(context.graph()); // Treat entities on addr:interpolation lines as points, not vertices (#3241)
89592 if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
89593 geometry = 'point';
89596 if (!counts[geometry]) counts[geometry] = 0;
89597 counts[geometry] += 1;
89600 return Object.keys(counts).sort(function (geom1, geom2) {
89601 return counts[geom2] - counts[geom1];
89605 function combinedEntityExtent() {
89606 return _entityIDs.reduce(function (extent, entityID) {
89607 var entity = context.graph().entity(entityID);
89608 return extent.extend(entity.extent(context.graph()));
89612 return utilRebind(presetList, dispatch$1, 'on');
89615 function uiInspector(context) {
89616 var presetList = uiPresetList(context);
89617 var entityEditor = uiEntityEditor(context);
89618 var wrap = select(null),
89619 presetPane = select(null),
89620 editorPane = select(null);
89621 var _state = 'select';
89625 var _newFeature = false;
89627 function inspector(selection) {
89628 presetList.entityIDs(_entityIDs).autofocus(_newFeature).on('choose', inspector.setPreset).on('cancel', function () {
89629 inspector.setPreset();
89631 entityEditor.state(_state).entityIDs(_entityIDs).on('choose', inspector.showList);
89632 wrap = selection.selectAll('.panewrap').data([0]);
89633 var enter = wrap.enter().append('div').attr('class', 'panewrap');
89634 enter.append('div').attr('class', 'preset-list-pane pane');
89635 enter.append('div').attr('class', 'entity-editor-pane pane');
89636 wrap = wrap.merge(enter);
89637 presetPane = wrap.selectAll('.preset-list-pane');
89638 editorPane = wrap.selectAll('.entity-editor-pane');
89640 function shouldDefaultToPresetList() {
89641 // always show the inspector on hover
89642 if (_state !== 'select') return false; // can only change preset on single selection
89644 if (_entityIDs.length !== 1) return false;
89645 var entityID = _entityIDs[0];
89646 var entity = context.hasEntity(entityID);
89647 if (!entity) return false; // default to inspector if there are already tags
89649 if (entity.hasNonGeometryTags()) return false; // prompt to select preset if feature is new and untagged
89651 if (_newFeature) return true; // all existing features except vertices should default to inspector
89653 if (entity.geometry(context.graph()) !== 'vertex') return false; // show vertex relations if any
89655 if (context.graph().parentRelations(entity).length) return false; // show vertex issues if there are any
89657 if (context.validator().getEntityIssues(entityID).length) return false; // show turn retriction editor for junction vertices
89659 if (entity.isHighwayIntersection(context.graph())) return false; // otherwise show preset list for uninteresting vertices
89664 if (shouldDefaultToPresetList()) {
89665 wrap.style('right', '-100%');
89666 editorPane.classed('hide', true);
89667 presetPane.classed('hide', false).call(presetList);
89669 wrap.style('right', '0%');
89670 presetPane.classed('hide', true);
89671 editorPane.classed('hide', false).call(entityEditor);
89674 var footer = selection.selectAll('.footer').data([0]);
89675 footer = footer.enter().append('div').attr('class', 'footer').merge(footer);
89676 footer.call(uiViewOnOSM(context).what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0])));
89679 inspector.showList = function (presets) {
89680 presetPane.classed('hide', false);
89681 wrap.transition().styleTween('right', function () {
89682 return interpolate('0%', '-100%');
89683 }).on('end', function () {
89684 editorPane.classed('hide', true);
89688 presetList.presets(presets);
89691 presetPane.call(presetList.autofocus(true));
89694 inspector.setPreset = function (preset) {
89695 // upon setting multipolygon, go to the area preset list instead of the editor
89696 if (preset && preset.id === 'type/multipolygon') {
89697 presetPane.call(presetList.autofocus(true));
89699 editorPane.classed('hide', false);
89700 wrap.transition().styleTween('right', function () {
89701 return interpolate('-100%', '0%');
89702 }).on('end', function () {
89703 presetPane.classed('hide', true);
89707 entityEditor.presets([preset]);
89710 editorPane.call(entityEditor);
89714 inspector.state = function (val) {
89715 if (!arguments.length) return _state;
89717 entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector
89719 context.container().selectAll('.field-help-body').remove();
89723 inspector.entityIDs = function (val) {
89724 if (!arguments.length) return _entityIDs;
89729 inspector.newFeature = function (val) {
89730 if (!arguments.length) return _newFeature;
89738 function uiSidebar(context) {
89739 var inspector = uiInspector(context);
89740 var dataEditor = uiDataEditor(context);
89741 var noteEditor = uiNoteEditor(context);
89742 var improveOsmEditor = uiImproveOsmEditor(context);
89743 var keepRightEditor = uiKeepRightEditor(context);
89744 var osmoseEditor = uiOsmoseEditor(context);
89748 var _wasData = false;
89749 var _wasNote = false;
89750 var _wasQaItem = false; // use pointer events on supported platforms; fallback to mouse events
89752 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
89754 function sidebar(selection) {
89755 var container = context.container();
89756 var minWidth = 240;
89758 var containerWidth;
89759 var dragOffset; // Set the initial width constraints
89761 selection.style('min-width', minWidth + 'px').style('max-width', '400px').style('width', '33.3333%');
89762 var resizer = selection.append('div').attr('class', 'sidebar-resizer').on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
89763 var downPointerId, lastClientX, containerLocGetter;
89765 function pointerdown(d3_event) {
89766 if (downPointerId) return;
89767 if ('button' in d3_event && d3_event.button !== 0) return;
89768 downPointerId = d3_event.pointerId || 'mouse';
89769 lastClientX = d3_event.clientX;
89770 containerLocGetter = utilFastMouse(container.node()); // offset from edge of sidebar-resizer
89772 dragOffset = utilFastMouse(resizer.node())(d3_event)[0] - 1;
89773 sidebarWidth = selection.node().getBoundingClientRect().width;
89774 containerWidth = container.node().getBoundingClientRect().width;
89775 var widthPct = sidebarWidth / containerWidth * 100;
89776 selection.style('width', widthPct + '%') // lock in current width
89777 .style('max-width', '85%'); // but allow larger widths
89779 resizer.classed('dragging', true);
89780 select(window).on('touchmove.sidebar-resizer', function (d3_event) {
89781 // disable page scrolling while resizing on touch input
89782 d3_event.preventDefault();
89785 }).on(_pointerPrefix + 'move.sidebar-resizer', pointermove).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
89788 function pointermove(d3_event) {
89789 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
89790 d3_event.preventDefault();
89791 var dx = d3_event.clientX - lastClientX;
89792 lastClientX = d3_event.clientX;
89793 var isRTL = _mainLocalizer.textDirection() === 'rtl';
89794 var scaleX = isRTL ? 0 : 1;
89795 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
89796 var x = containerLocGetter(d3_event)[0] - dragOffset;
89797 sidebarWidth = isRTL ? containerWidth - x : x;
89798 var isCollapsed = selection.classed('collapsed');
89799 var shouldCollapse = sidebarWidth < minWidth;
89800 selection.classed('collapsed', shouldCollapse);
89802 if (shouldCollapse) {
89803 if (!isCollapsed) {
89804 selection.style(xMarginProperty, '-400px').style('width', '400px');
89805 context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
89808 var widthPct = sidebarWidth / containerWidth * 100;
89809 selection.style(xMarginProperty, null).style('width', widthPct + '%');
89812 context.ui().onResize([-sidebarWidth * scaleX, 0]);
89814 context.ui().onResize([-dx * scaleX, 0]);
89819 function pointerup(d3_event) {
89820 if (downPointerId !== (d3_event.pointerId || 'mouse')) return;
89821 downPointerId = null;
89822 resizer.classed('dragging', false);
89823 select(window).on('touchmove.sidebar-resizer', null).on(_pointerPrefix + 'move.sidebar-resizer', null).on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
89826 var featureListWrap = selection.append('div').attr('class', 'feature-list-pane').call(uiFeatureList(context));
89827 var inspectorWrap = selection.append('div').attr('class', 'inspector-hidden inspector-wrap');
89829 var hoverModeSelect = function hoverModeSelect(targets) {
89830 context.container().selectAll('.feature-list-item button').classed('hover', false);
89832 if (context.selectedIDs().length > 1 && targets && targets.length) {
89833 var elements = context.container().selectAll('.feature-list-item button').filter(function (node) {
89834 return targets.indexOf(node) !== -1;
89837 if (!elements.empty()) {
89838 elements.classed('hover', true);
89843 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
89845 function hover(targets) {
89846 var datum = targets && targets.length && targets[0];
89848 if (datum && datum.__featurehash__) {
89849 // hovering on data
89851 sidebar.show(dataEditor.datum(datum));
89852 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89853 } else if (datum instanceof osmNote) {
89854 if (context.mode().id === 'drag-note') return;
89856 var osm = services.osm;
89859 datum = osm.getNote(datum.id); // marker may contain stale data - get latest
89862 sidebar.show(noteEditor.note(datum));
89863 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89864 } else if (datum instanceof QAItem) {
89866 var errService = services[datum.service];
89869 // marker may contain stale data - get latest
89870 datum = errService.getError(datum.id);
89871 } // Currently only three possible services
89876 if (datum.service === 'keepRight') {
89877 errEditor = keepRightEditor;
89878 } else if (datum.service === 'osmose') {
89879 errEditor = osmoseEditor;
89881 errEditor = improveOsmEditor;
89884 context.container().selectAll('.qaItem.' + datum.service).classed('hover', function (d) {
89885 return d.id === datum.id;
89887 sidebar.show(errEditor.error(datum));
89888 selection.selectAll('.sidebar-component').classed('inspector-hover', true);
89889 } else if (!_current && datum instanceof osmEntity) {
89890 featureListWrap.classed('inspector-hidden', true);
89891 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', true);
89893 if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
89894 inspector.state('hover').entityIDs([datum.id]).newFeature(false);
89895 inspectorWrap.call(inspector);
89897 } else if (!_current) {
89898 featureListWrap.classed('inspector-hidden', false);
89899 inspectorWrap.classed('inspector-hidden', true);
89900 inspector.state('hide');
89901 } else if (_wasData || _wasNote || _wasQaItem) {
89904 _wasQaItem = false;
89905 context.container().selectAll('.note').classed('hover', false);
89906 context.container().selectAll('.qaItem').classed('hover', false);
89911 sidebar.hover = throttle(hover, 200);
89913 sidebar.intersects = function (extent) {
89914 var rect = selection.node().getBoundingClientRect();
89915 return extent.intersects([context.projection.invert([0, rect.height]), context.projection.invert([rect.width, 0])]);
89918 sidebar.select = function (ids, newFeature) {
89921 if (ids && ids.length) {
89922 var entity = ids.length === 1 && context.entity(ids[0]);
89924 if (entity && newFeature && selection.classed('collapsed')) {
89925 // uncollapse the sidebar
89926 var extent = entity.extent(context.graph());
89927 sidebar.expand(sidebar.intersects(extent));
89930 featureListWrap.classed('inspector-hidden', true);
89931 inspectorWrap.classed('inspector-hidden', false).classed('inspector-hover', false); // reload the UI even if the ids are the same since the entities
89932 // themselves may have changed
89934 inspector.state('select').entityIDs(ids).newFeature(newFeature);
89935 inspectorWrap.call(inspector);
89937 inspector.state('hide');
89941 sidebar.showPresetList = function () {
89942 inspector.showList();
89945 sidebar.show = function (component, element) {
89946 featureListWrap.classed('inspector-hidden', true);
89947 inspectorWrap.classed('inspector-hidden', true);
89948 if (_current) _current.remove();
89949 _current = selection.append('div').attr('class', 'sidebar-component').call(component, element);
89952 sidebar.hide = function () {
89953 featureListWrap.classed('inspector-hidden', false);
89954 inspectorWrap.classed('inspector-hidden', true);
89955 if (_current) _current.remove();
89959 sidebar.expand = function (moveMap) {
89960 if (selection.classed('collapsed')) {
89961 sidebar.toggle(moveMap);
89965 sidebar.collapse = function (moveMap) {
89966 if (!selection.classed('collapsed')) {
89967 sidebar.toggle(moveMap);
89971 sidebar.toggle = function (moveMap) {
89972 // Don't allow sidebar to toggle when the user is in the walkthrough.
89973 if (context.inIntro()) return;
89974 var isCollapsed = selection.classed('collapsed');
89975 var isCollapsing = !isCollapsed;
89976 var isRTL = _mainLocalizer.textDirection() === 'rtl';
89977 var scaleX = isRTL ? 0 : 1;
89978 var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
89979 sidebarWidth = selection.node().getBoundingClientRect().width; // switch from % to px
89981 selection.style('width', sidebarWidth + 'px');
89982 var startMargin, endMargin, lastMargin;
89984 if (isCollapsing) {
89985 startMargin = lastMargin = 0;
89986 endMargin = -sidebarWidth;
89988 startMargin = lastMargin = -sidebarWidth;
89992 if (!isCollapsing) {
89993 // unhide the sidebar's content before it transitions onscreen
89994 selection.classed('collapsed', isCollapsing);
89997 selection.transition().style(xMarginProperty, endMargin + 'px').tween('panner', function () {
89998 var i = d3_interpolateNumber(startMargin, endMargin);
89999 return function (t) {
90000 var dx = lastMargin - Math.round(i(t));
90001 lastMargin = lastMargin - dx;
90002 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
90004 }).on('end', function () {
90005 if (isCollapsing) {
90006 // hide the sidebar's content after it transitions offscreen
90007 selection.classed('collapsed', isCollapsing);
90008 } // switch back from px to %
90011 if (!isCollapsing) {
90012 var containerWidth = container.node().getBoundingClientRect().width;
90013 var widthPct = sidebarWidth / containerWidth * 100;
90014 selection.style(xMarginProperty, null).style('width', widthPct + '%');
90017 }; // toggle the sidebar collapse when double-clicking the resizer
90020 resizer.on('dblclick', function (d3_event) {
90021 d3_event.preventDefault();
90023 if (d3_event.sourceEvent) {
90024 d3_event.sourceEvent.preventDefault();
90028 }); // ensure hover sidebar is closed when zooming out beyond editable zoom
90030 context.map().on('crossEditableZoom.sidebar', function (within) {
90031 if (!within && !selection.select('.inspector-hover').empty()) {
90037 sidebar.showPresetList = function () {};
90039 sidebar.hover = function () {};
90041 sidebar.hover.cancel = function () {};
90043 sidebar.intersects = function () {};
90045 sidebar.select = function () {};
90047 sidebar.show = function () {};
90049 sidebar.hide = function () {};
90051 sidebar.expand = function () {};
90053 sidebar.collapse = function () {};
90055 sidebar.toggle = function () {};
90060 function uiSourceSwitch(context) {
90063 function click(d3_event) {
90064 d3_event.preventDefault();
90065 var osm = context.connection();
90067 if (context.inIntro()) return;
90068 if (context.history().hasChanges() && !window.confirm(_t('source_switch.lose_changes'))) return;
90069 var isLive = select(this).classed('live');
90071 context.enter(modeBrowse(context));
90072 context.history().clearSaved(); // remove saved history
90074 context.flush(); // remove stored data
90076 select(this).html(isLive ? _t.html('source_switch.live') : _t.html('source_switch.dev')).classed('live', isLive).classed('chip', isLive);
90077 osm["switch"](isLive ? keys[0] : keys[1]); // switch connection (warning: dispatches 'change' event)
90080 var sourceSwitch = function sourceSwitch(selection) {
90081 selection.append('a').attr('href', '#').html(_t.html('source_switch.live')).attr('class', 'live chip').on('click', click);
90084 sourceSwitch.keys = function (_) {
90085 if (!arguments.length) return keys;
90087 return sourceSwitch;
90090 return sourceSwitch;
90093 function uiSpinner(context) {
90094 var osm = context.connection();
90095 return function (selection) {
90096 var img = selection.append('img').attr('src', context.imagePath('loader-black.gif')).style('opacity', 0);
90099 osm.on('loading.spinner', function () {
90100 img.transition().style('opacity', 1);
90101 }).on('loaded.spinner', function () {
90102 img.transition().style('opacity', 0);
90108 function uiSplash(context) {
90109 return function (selection) {
90110 // Exception - if there are restorable changes, skip this splash screen.
90111 // This is because we currently only support one `uiModal` at a time
90112 // and we need to show them `uiRestore`` instead of this one.
90113 if (context.history().hasRestorableChanges()) return; // If user has not seen this version of the privacy policy, show the splash again.
90115 var updateMessage = '';
90116 var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
90117 var showSplash = !corePreferences('sawSplash');
90119 if (sawPrivacyVersion !== context.privacyVersion) {
90120 updateMessage = _t('splash.privacy_update');
90124 if (!showSplash) return;
90125 corePreferences('sawSplash', true);
90126 corePreferences('sawPrivacyVersion', context.privacyVersion); // fetch intro graph data now, while user is looking at the splash screen
90128 _mainFileFetcher.get('intro_graph');
90129 var modalSelection = uiModal(selection);
90130 modalSelection.select('.modal').attr('class', 'modal-splash modal');
90131 var introModal = modalSelection.select('.content').append('div').attr('class', 'fillL');
90132 introModal.append('div').attr('class', 'modal-section').append('h3').html(_t.html('splash.welcome'));
90133 var modalSection = introModal.append('div').attr('class', 'modal-section');
90134 modalSection.append('p').html(_t.html('splash.text', {
90135 version: context.version,
90136 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
90137 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
90139 modalSection.append('p').html(_t.html('splash.privacy', {
90140 updateMessage: updateMessage,
90141 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' + _t('splash.privacy_policy') + '</a>'
90143 var buttonWrap = introModal.append('div').attr('class', 'modal-actions');
90144 var walkthrough = buttonWrap.append('button').attr('class', 'walkthrough').on('click', function () {
90145 context.container().call(uiIntro(context));
90146 modalSelection.close();
90148 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
90149 walkthrough.append('div').html(_t.html('splash.walkthrough'));
90150 var startEditing = buttonWrap.append('button').attr('class', 'start-editing').on('click', modalSelection.close);
90151 startEditing.append('svg').attr('class', 'logo logo-features').append('use').attr('xlink:href', '#iD-logo-features');
90152 startEditing.append('div').html(_t.html('splash.start'));
90153 modalSelection.select('button.close').attr('class', 'hide');
90157 function uiStatus(context) {
90158 var osm = context.connection();
90159 return function (selection) {
90162 function update(err, apiStatus) {
90163 selection.html('');
90166 if (apiStatus === 'connectionSwitched') {
90167 // if the connection was just switched, we can't rely on
90168 // the status (we're getting the status of the previous api)
90170 } else if (apiStatus === 'rateLimited') {
90171 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) {
90172 d3_event.preventDefault();
90173 osm.authenticate();
90176 // don't allow retrying too rapidly
90177 var throttledRetry = throttle(function () {
90178 // try loading the visible tiles
90179 context.loadTiles(context.projection); // manually reload the status too in case all visible tiles were already loaded
90181 osm.reloadApiStatus();
90182 }, 2000); // eslint-disable-next-line no-warning-comments
90183 // TODO: nice messages for different error types
90186 selection.html(_t.html('osm_api_status.message.error') + ' ').append('a').attr('href', '#') // let the user manually retry their connection directly
90187 .html(_t.html('osm_api_status.retry')).on('click.retry', function (d3_event) {
90188 d3_event.preventDefault();
90192 } else if (apiStatus === 'readonly') {
90193 selection.html(_t.html('osm_api_status.message.readonly'));
90194 } else if (apiStatus === 'offline') {
90195 selection.html(_t.html('osm_api_status.message.offline'));
90198 selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
90201 osm.on('apiStatusChange.uiStatus', update); // reload the status periodically regardless of other factors
90203 window.setInterval(function () {
90204 osm.reloadApiStatus();
90205 }, 90000); // load the initial status in case no OSM data was loaded yet
90207 osm.reloadApiStatus();
90211 function modeDrawArea(context, wayID, startGraph, button) {
90216 var behavior = behaviorDrawWay(context, wayID, mode, startGraph).on('rejectedSelfIntersection.modeDrawArea', function () {
90217 context.ui().flash.iconName('#iD-icon-no').label(_t('self_intersection.error.areas'))();
90219 mode.wayID = wayID;
90221 mode.enter = function () {
90222 context.install(behavior);
90225 mode.exit = function () {
90226 context.uninstall(behavior);
90229 mode.selectedIDs = function () {
90233 mode.activeID = function () {
90234 return behavior && behavior.activeID() || [];
90240 function modeAddArea(context, mode) {
90241 mode.id = 'add-area';
90242 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
90243 var defaultTags = {
90246 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'area');
90248 function actionClose(wayId) {
90249 return function (graph) {
90250 return graph.replace(graph.entity(wayId).close());
90254 function start(loc) {
90255 var startGraph = context.graph();
90256 var node = osmNode({
90262 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
90263 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90266 function startFromWay(loc, edge) {
90267 var startGraph = context.graph();
90268 var node = osmNode({
90274 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id), actionAddMidpoint({
90278 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90281 function startFromNode(node) {
90282 var startGraph = context.graph();
90286 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id), actionClose(way.id));
90287 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
90290 mode.enter = function () {
90291 context.install(behavior);
90294 mode.exit = function () {
90295 context.uninstall(behavior);
90301 function modeAddLine(context, mode) {
90302 mode.id = 'add-line';
90303 var behavior = behaviorAddWay(context).on('start', start).on('startFromWay', startFromWay).on('startFromNode', startFromNode);
90304 var defaultTags = {};
90305 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'line');
90307 function start(loc) {
90308 var startGraph = context.graph();
90309 var node = osmNode({
90315 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id));
90316 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90319 function startFromWay(loc, edge) {
90320 var startGraph = context.graph();
90321 var node = osmNode({
90327 context.perform(actionAddEntity(node), actionAddEntity(way), actionAddVertex(way.id, node.id), actionAddMidpoint({
90331 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90334 function startFromNode(node) {
90335 var startGraph = context.graph();
90339 context.perform(actionAddEntity(way), actionAddVertex(way.id, node.id));
90340 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
90343 mode.enter = function () {
90344 context.install(behavior);
90347 mode.exit = function () {
90348 context.uninstall(behavior);
90354 function modeAddPoint(context, mode) {
90355 mode.id = 'add-point';
90356 var behavior = behaviorDraw(context).on('click', add).on('clickWay', addWay).on('clickNode', addNode).on('cancel', cancel).on('finish', cancel);
90357 var defaultTags = {};
90358 if (mode.preset) defaultTags = mode.preset.setTags(defaultTags, 'point');
90360 function add(loc) {
90361 var node = osmNode({
90365 context.perform(actionAddEntity(node), _t('operations.add.annotation.point'));
90366 enterSelectMode(node);
90369 function addWay(loc, edge) {
90370 var node = osmNode({
90373 context.perform(actionAddMidpoint({
90376 }, node), _t('operations.add.annotation.vertex'));
90377 enterSelectMode(node);
90380 function enterSelectMode(node) {
90381 context.enter(modeSelect(context, [node.id]).newFeature(true));
90384 function addNode(node) {
90385 if (Object.keys(defaultTags).length === 0) {
90386 enterSelectMode(node);
90390 var tags = Object.assign({}, node.tags); // shallow copy
90392 for (var key in defaultTags) {
90393 tags[key] = defaultTags[key];
90396 context.perform(actionChangeTags(node.id, tags), _t('operations.add.annotation.point'));
90397 enterSelectMode(node);
90400 function cancel() {
90401 context.enter(modeBrowse(context));
90404 mode.enter = function () {
90405 context.install(behavior);
90408 mode.exit = function () {
90409 context.uninstall(behavior);
90415 function modeAddNote(context) {
90419 description: _t.html('modes.add_note.description'),
90420 key: _t('modes.add_note.key')
90422 var behavior = behaviorDraw(context).on('click', add).on('cancel', cancel).on('finish', cancel);
90424 function add(loc) {
90425 var osm = services.osm;
90427 var note = osmNote({
90432 osm.replaceNote(note); // force a reraw (there is no history change that would otherwise do this)
90434 context.map().pan([0, 0]);
90435 context.selectedNoteID(note.id).enter(modeSelectNote(context, note.id).newFeature(true));
90438 function cancel() {
90439 context.enter(modeBrowse(context));
90442 mode.enter = function () {
90443 context.install(behavior);
90446 mode.exit = function () {
90447 context.uninstall(behavior);
90453 function uiConflicts(context) {
90454 var dispatch$1 = dispatch('cancel', 'save');
90455 var keybinding = utilKeybinding('conflicts');
90461 var _shownConflictIndex;
90463 function keybindingOn() {
90464 select(document).call(keybinding.on('⎋', cancel, true));
90467 function keybindingOff() {
90468 select(document).call(keybinding.unbind);
90471 function tryAgain() {
90473 dispatch$1.call('save');
90476 function cancel() {
90478 dispatch$1.call('cancel');
90481 function conflicts(selection) {
90483 var headerEnter = selection.selectAll('.header').data([0]).enter().append('div').attr('class', 'header fillL');
90484 headerEnter.append('button').attr('class', 'fr').on('click', cancel).call(svgIcon('#iD-icon-close'));
90485 headerEnter.append('h3').html(_t.html('save.conflict.header'));
90486 var bodyEnter = selection.selectAll('.body').data([0]).enter().append('div').attr('class', 'body fillL');
90487 var conflictsHelpEnter = bodyEnter.append('div').attr('class', 'conflicts-help').html(_t.html('save.conflict.help')); // Download changes link
90489 var detected = utilDetect();
90490 var changeset = new osmChangeset();
90491 delete changeset.id; // Export without changeset_id
90493 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
90494 var blob = new Blob([data], {
90495 type: 'text/xml;charset=utf-8;'
90497 var fileName = 'changes.osc';
90498 var linkEnter = conflictsHelpEnter.selectAll('.download-changes').append('a').attr('class', 'download-changes');
90500 if (detected.download) {
90501 // All except IE11 and Edge
90502 linkEnter // download the data as a file
90503 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
90506 linkEnter // open data uri in a new tab
90507 .attr('target', '_blank').on('click.download', function () {
90508 navigator.msSaveBlob(blob, fileName);
90512 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('save.conflict.download_changes'));
90513 bodyEnter.append('div').attr('class', 'conflict-container fillL3').call(showConflict, 0);
90514 bodyEnter.append('div').attr('class', 'conflicts-done').attr('opacity', 0).style('display', 'none').html(_t.html('save.conflict.done'));
90515 var buttonsEnter = bodyEnter.append('div').attr('class', 'buttons col12 joined conflicts-buttons');
90516 buttonsEnter.append('button').attr('disabled', _conflictList.length > 1).attr('class', 'action conflicts-button col6').html(_t.html('save.title')).on('click.try_again', tryAgain);
90517 buttonsEnter.append('button').attr('class', 'secondary-action conflicts-button col6').html(_t.html('confirm.cancel')).on('click.cancel', cancel);
90520 function showConflict(selection, index) {
90521 index = utilWrap(index, _conflictList.length);
90522 _shownConflictIndex = index;
90523 var parent = select(selection.node().parentNode); // enable save button if this is the last conflict being reviewed..
90525 if (index === _conflictList.length - 1) {
90526 window.setTimeout(function () {
90527 parent.select('.conflicts-button').attr('disabled', null);
90528 parent.select('.conflicts-done').transition().attr('opacity', 1).style('display', 'block');
90532 var conflict = selection.selectAll('.conflict').data([_conflictList[index]]);
90533 conflict.exit().remove();
90534 var conflictEnter = conflict.enter().append('div').attr('class', 'conflict');
90535 conflictEnter.append('h4').attr('class', 'conflict-count').html(_t.html('save.conflict.count', {
90537 total: _conflictList.length
90539 conflictEnter.append('a').attr('class', 'conflict-description').attr('href', '#').html(function (d) {
90541 }).on('click', function (d3_event, d) {
90542 d3_event.preventDefault();
90543 zoomToEntity(d.id);
90545 var details = conflictEnter.append('div').attr('class', 'conflict-detail-container');
90546 details.append('ul').attr('class', 'conflict-detail-list').selectAll('li').data(function (d) {
90547 return d.details || [];
90548 }).enter().append('li').attr('class', 'conflict-detail-item').html(function (d) {
90551 details.append('div').attr('class', 'conflict-choices').call(addChoices);
90552 details.append('div').attr('class', 'conflict-nav-buttons joined cf').selectAll('button').data(['previous', 'next']).enter().append('button').html(function (d) {
90553 return _t.html('save.conflict.' + d);
90554 }).attr('class', 'conflict-nav-button action col6').attr('disabled', function (d, i) {
90555 return i === 0 && index === 0 || i === 1 && index === _conflictList.length - 1 || null;
90556 }).on('click', function (d3_event, d) {
90557 d3_event.preventDefault();
90558 var container = parent.selectAll('.conflict-container');
90559 var sign = d === 'previous' ? -1 : 1;
90560 container.selectAll('.conflict').remove();
90561 container.call(showConflict, index + sign);
90565 function addChoices(selection) {
90566 var choices = selection.append('ul').attr('class', 'layer-list').selectAll('li').data(function (d) {
90567 return d.choices || [];
90570 var choicesEnter = choices.enter().append('li').attr('class', 'layer');
90571 var labelEnter = choicesEnter.append('label');
90572 labelEnter.append('input').attr('type', 'radio').attr('name', function (d) {
90574 }).on('change', function (d3_event, d) {
90575 var ul = this.parentNode.parentNode.parentNode;
90576 ul.__data__.chosen = d.id;
90577 choose(d3_event, ul, d);
90579 labelEnter.append('span').html(function (d) {
90583 choicesEnter.merge(choices).each(function (d) {
90584 var ul = this.parentNode;
90586 if (ul.__data__.chosen === d.id) {
90587 choose(null, ul, d);
90592 function choose(d3_event, ul, datum) {
90593 if (d3_event) d3_event.preventDefault();
90594 select(ul).selectAll('li').classed('active', function (d) {
90595 return d === datum;
90596 }).selectAll('input').property('checked', function (d) {
90597 return d === datum;
90599 var extent = geoExtent();
90601 entity = context.graph().hasEntity(datum.id);
90602 if (entity) extent._extend(entity.extent(context.graph()));
90604 entity = context.graph().hasEntity(datum.id);
90605 if (entity) extent._extend(entity.extent(context.graph()));
90606 zoomToEntity(datum.id, extent);
90609 function zoomToEntity(id, extent) {
90610 context.surface().selectAll('.hover').classed('hover', false);
90611 var entity = context.graph().hasEntity(id);
90615 context.map().trimmedExtent(extent);
90617 context.map().zoomToEase(entity);
90620 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
90622 } // The conflict list should be an array of objects like:
90625 // name: entityName(local),
90626 // details: merge.conflicts(),
90629 // choice(id, keepMine, forceLocal),
90630 // choice(id, keepTheirs, forceRemote)
90635 conflicts.conflictList = function (_) {
90636 if (!arguments.length) return _conflictList;
90641 conflicts.origChanges = function (_) {
90642 if (!arguments.length) return _origChanges;
90647 conflicts.shownEntityIds = function () {
90648 if (_conflictList && typeof _shownConflictIndex === 'number') {
90649 return [_conflictList[_shownConflictIndex].id];
90655 return utilRebind(conflicts, dispatch$1, 'on');
90658 function uiConfirm(selection) {
90659 var modalSelection = uiModal(selection);
90660 modalSelection.select('.modal').classed('modal-alert', true);
90661 var section = modalSelection.select('.content');
90662 section.append('div').attr('class', 'modal-section header');
90663 section.append('div').attr('class', 'modal-section message-text');
90664 var buttons = section.append('div').attr('class', 'modal-section buttons cf');
90666 modalSelection.okButton = function () {
90667 buttons.append('button').attr('class', 'button ok-button action').on('click.confirm', function () {
90668 modalSelection.remove();
90669 }).html(_t.html('confirm.okay')).node().focus();
90670 return modalSelection;
90673 return modalSelection;
90676 function uiChangesetEditor(context) {
90677 var dispatch$1 = dispatch('change');
90678 var formFields = uiFormFields(context);
90679 var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
90687 function changesetEditor(selection) {
90691 function render(selection) {
90692 var initial = false;
90696 var presets = _mainPresetIndex;
90697 _fieldsArr = [uiField(context, presets.field('comment'), null, {
90700 }), uiField(context, presets.field('source'), null, {
90703 }), uiField(context, presets.field('hashtags'), null, {
90708 _fieldsArr.forEach(function (field) {
90709 field.on('change', function (t, onInput) {
90710 dispatch$1.call('change', field, undefined, t, onInput);
90715 _fieldsArr.forEach(function (field) {
90719 selection.call(formFields.fieldsArr(_fieldsArr));
90722 var commentField = selection.select('.form-field-comment textarea');
90723 var commentNode = commentField.node();
90726 commentNode.focus();
90727 commentNode.select();
90728 } // trigger a 'blur' event so that comment field can be cleaned
90729 // and checked for hashtags, even if retrieved from localstorage
90732 utilTriggerEvent(commentField, 'blur');
90733 var osm = context.connection();
90736 osm.userChangesets(function (err, changesets) {
90738 var comments = changesets.map(function (changeset) {
90739 var comment = changeset.tags.comment;
90744 }).filter(Boolean);
90745 commentField.call(commentCombo.data(utilArrayUniqBy(comments, 'title')));
90748 } // Add warning if comment mentions Google
90751 var hasGoogle = _tags.comment.match(/google/i);
90753 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning').data(hasGoogle ? [0] : []);
90754 commentWarning.exit().transition().duration(200).style('opacity', 0).remove();
90755 var commentEnter = commentWarning.enter().insert('div', '.tag-reference-body').attr('class', 'field-warning comment-warning').style('opacity', 0);
90756 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'));
90757 commentEnter.transition().duration(200).style('opacity', 1);
90760 changesetEditor.tags = function (_) {
90761 if (!arguments.length) return _tags;
90762 _tags = _; // Don't reset _fieldsArr here.
90764 return changesetEditor;
90767 changesetEditor.changesetID = function (_) {
90768 if (!arguments.length) return _changesetID;
90769 if (_changesetID === _) return changesetEditor;
90772 return changesetEditor;
90775 return utilRebind(changesetEditor, dispatch$1, 'on');
90778 function uiSectionChanges(context) {
90779 var detected = utilDetect();
90780 var _discardTags = {};
90781 _mainFileFetcher.get('discarded').then(function (d) {
90783 })["catch"](function () {
90786 var section = uiSection('changes-list', context).label(function () {
90787 var history = context.history();
90788 var summary = history.difference().summary();
90789 return _t('inspector.title_count', {
90790 title: _t.html('commit.changes'),
90791 count: summary.length
90793 }).disclosureContent(renderDisclosureContent);
90795 function renderDisclosureContent(selection) {
90796 var history = context.history();
90797 var summary = history.difference().summary();
90798 var container = selection.selectAll('.commit-section').data([0]);
90799 var containerEnter = container.enter().append('div').attr('class', 'commit-section');
90800 containerEnter.append('ul').attr('class', 'changeset-list');
90801 container = containerEnter.merge(container);
90802 var items = container.select('ul').selectAll('li').data(summary);
90803 var itemsEnter = items.enter().append('li').attr('class', 'change-item');
90804 var buttons = itemsEnter.append('button').on('mouseover', mouseover).on('mouseout', mouseout).on('click', click);
90805 buttons.each(function (d) {
90806 select(this).call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
90808 buttons.append('span').attr('class', 'change-type').html(function (d) {
90809 return _t.html('commit.' + d.changeType) + ' ';
90811 buttons.append('strong').attr('class', 'entity-type').html(function (d) {
90812 var matched = _mainPresetIndex.match(d.entity, d.graph);
90813 return matched && matched.name() || utilDisplayType(d.entity.id);
90815 buttons.append('span').attr('class', 'entity-name').html(function (d) {
90816 var name = utilDisplayName(d.entity) || '',
90823 return string += ' ' + name;
90825 items = itemsEnter.merge(items); // Download changeset link
90827 var changeset = new osmChangeset().update({
90830 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
90831 delete changeset.id; // Export without chnageset_id
90833 var data = JXON.stringify(changeset.osmChangeJXON(changes));
90834 var blob = new Blob([data], {
90835 type: 'text/xml;charset=utf-8;'
90837 var fileName = 'changes.osc';
90838 var linkEnter = container.selectAll('.download-changes').data([0]).enter().append('a').attr('class', 'download-changes');
90840 if (detected.download) {
90841 // All except IE11 and Edge
90842 linkEnter // download the data as a file
90843 .attr('href', window.URL.createObjectURL(blob)).attr('download', fileName);
90846 linkEnter // open data uri in a new tab
90847 .attr('target', '_blank').on('click.download', function () {
90848 navigator.msSaveBlob(blob, fileName);
90852 linkEnter.call(svgIcon('#iD-icon-load', 'inline')).append('span').html(_t.html('commit.download_changes'));
90854 function mouseover(d) {
90856 context.surface().selectAll(utilEntityOrMemberSelector([d.entity.id], context.graph())).classed('hover', true);
90860 function mouseout() {
90861 context.surface().selectAll('.hover').classed('hover', false);
90864 function click(d3_event, change) {
90865 if (change.changeType !== 'deleted') {
90866 var entity = change.entity;
90867 context.map().zoomToEase(entity);
90868 context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph())).classed('hover', true);
90876 function uiCommitWarnings(context) {
90877 function commitWarnings(selection) {
90878 var issuesBySeverity = context.validator().getIssuesBySeverity({
90881 includeDisabledRules: true
90884 for (var severity in issuesBySeverity) {
90885 var issues = issuesBySeverity[severity];
90886 var section = severity + '-section';
90887 var issueItem = severity + '-item';
90888 var container = selection.selectAll('.' + section).data(issues.length ? [0] : []);
90889 container.exit().remove();
90890 var containerEnter = container.enter().append('div').attr('class', 'modal-section ' + section + ' fillL2');
90891 containerEnter.append('h3').html(severity === 'warning' ? _t.html('commit.warnings') : _t.html('commit.errors'));
90892 containerEnter.append('ul').attr('class', 'changeset-list');
90893 container = containerEnter.merge(container);
90894 var items = container.select('ul').selectAll('li').data(issues, function (d) {
90897 items.exit().remove();
90898 var itemsEnter = items.enter().append('li').attr('class', issueItem);
90899 var buttons = itemsEnter.append('button').on('mouseover', function (d3_event, d) {
90901 context.surface().selectAll(utilEntityOrMemberSelector(d.entityIds, context.graph())).classed('hover', true);
90903 }).on('mouseout', function () {
90904 context.surface().selectAll('.hover').classed('hover', false);
90905 }).on('click', function (d3_event, d) {
90906 context.validator().focusIssue(d);
90908 buttons.call(svgIcon('#iD-icon-alert', 'pre-text'));
90909 buttons.append('strong').attr('class', 'issue-message');
90910 buttons.filter(function (d) {
90912 }).call(uiTooltip().title(function (d) {
90914 }).placement('top'));
90915 items = itemsEnter.merge(items);
90916 items.selectAll('.issue-message').html(function (d) {
90917 return d.message(context);
90922 return commitWarnings;
90925 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
90926 // from https://stackoverflow.com/a/25575009
90928 var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
90929 function uiCommit(context) {
90930 var dispatch$1 = dispatch('cancel');
90936 var changesetEditor = uiChangesetEditor(context).on('change', changeTags);
90937 var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context).on('change', changeTags).readOnlyTags(readOnlyTags);
90938 var commitChanges = uiSectionChanges(context);
90939 var commitWarnings = uiCommitWarnings(context);
90941 function commit(selection) {
90942 _selection = selection; // Initialize changeset if one does not exist yet.
90944 if (!context.changeset) initChangeset();
90945 loadDerivedChangesetTags();
90946 selection.call(render);
90949 function initChangeset() {
90950 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
90951 var commentDate = +corePreferences('commentDate') || 0;
90952 var currDate = Date.now();
90953 var cutoff = 2 * 86400 * 1000; // 2 days
90955 if (commentDate > currDate || currDate - commentDate > cutoff) {
90956 corePreferences('comment', null);
90957 corePreferences('hashtags', null);
90958 corePreferences('source', null);
90959 } // load in explicitly-set values, if any
90962 if (context.defaultChangesetComment()) {
90963 corePreferences('comment', context.defaultChangesetComment());
90964 corePreferences('commentDate', Date.now());
90967 if (context.defaultChangesetSource()) {
90968 corePreferences('source', context.defaultChangesetSource());
90969 corePreferences('commentDate', Date.now());
90972 if (context.defaultChangesetHashtags()) {
90973 corePreferences('hashtags', context.defaultChangesetHashtags());
90974 corePreferences('commentDate', Date.now());
90977 var detected = utilDetect();
90979 comment: corePreferences('comment') || '',
90980 created_by: context.cleanTagValue('iD ' + context.version),
90981 host: context.cleanTagValue(detected.host),
90982 locale: context.cleanTagValue(_mainLocalizer.localeCode())
90983 }; // call findHashtags initially - this will remove stored
90984 // hashtags if any hashtags are found in the comment - #4304
90986 findHashtags(tags, true);
90987 var hashtags = corePreferences('hashtags');
90990 tags.hashtags = hashtags;
90993 var source = corePreferences('source');
90996 tags.source = source;
90999 var photoOverlaysUsed = context.history().photoOverlaysUsed();
91001 if (photoOverlaysUsed.length) {
91002 var sources = (tags.source || '').split(';'); // include this tag for any photo layer
91004 if (sources.indexOf('streetlevel imagery') === -1) {
91005 sources.push('streetlevel imagery');
91006 } // add the photo overlays used during editing as sources
91009 photoOverlaysUsed.forEach(function (photoOverlay) {
91010 if (sources.indexOf(photoOverlay) === -1) {
91011 sources.push(photoOverlay);
91014 tags.source = context.cleanTagValue(sources.join(';'));
91017 context.changeset = new osmChangeset({
91020 } // Calculates read-only metadata tags based on the user's editing session and applies
91021 // them to the changeset.
91024 function loadDerivedChangesetTags() {
91025 var osm = context.connection();
91027 var tags = Object.assign({}, context.changeset.tags); // shallow copy
91028 // assign tags for imagery used
91030 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
91031 tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes
91033 var osmClosed = osm.getClosedIDs();
91036 if (osmClosed.length) {
91037 tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
91040 if (services.keepRight) {
91041 var krClosed = services.keepRight.getClosedIDs();
91043 if (krClosed.length) {
91044 tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
91048 if (services.improveOSM) {
91049 var iOsmClosed = services.improveOSM.getClosedCounts();
91051 for (itemType in iOsmClosed) {
91052 tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
91056 if (services.osmose) {
91057 var osmoseClosed = services.osmose.getClosedCounts();
91059 for (itemType in osmoseClosed) {
91060 tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
91062 } // remove existing issue counts
91065 for (var key in tags) {
91066 if (key.match(/(^warnings:)|(^resolved:)/)) {
91071 function addIssueCounts(issues, prefix) {
91072 var issuesByType = utilArrayGroupBy(issues, 'type');
91074 for (var issueType in issuesByType) {
91075 var issuesOfType = issuesByType[issueType];
91077 if (issuesOfType[0].subtype) {
91078 var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
91080 for (var issueSubtype in issuesBySubtype) {
91081 var issuesOfSubtype = issuesBySubtype[issueSubtype];
91082 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
91085 tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
91088 } // add counts of warnings generated by the user's edits
91091 var warnings = context.validator().getIssuesBySeverity({
91094 includeIgnored: true,
91095 includeDisabledRules: true
91097 addIssueCounts(warnings, 'warnings'); // add counts of issues resolved by the user's edits
91099 var resolvedIssues = context.validator().getResolvedIssues();
91100 addIssueCounts(resolvedIssues, 'resolved');
91101 context.changeset = context.changeset.update({
91106 function render(selection) {
91107 var osm = context.connection();
91109 var header = selection.selectAll('.header').data([0]);
91110 var headerTitle = header.enter().append('div').attr('class', 'header fillL');
91111 headerTitle.append('div').append('h3').html(_t.html('commit.title'));
91112 headerTitle.append('button').attr('class', 'close').on('click', function () {
91113 dispatch$1.call('cancel', this);
91114 }).call(svgIcon('#iD-icon-close'));
91115 var body = selection.selectAll('.body').data([0]);
91116 body = body.enter().append('div').attr('class', 'body').merge(body); // Changeset Section
91118 var changesetSection = body.selectAll('.changeset-editor').data([0]);
91119 changesetSection = changesetSection.enter().append('div').attr('class', 'modal-section changeset-editor').merge(changesetSection);
91120 changesetSection.call(changesetEditor.changesetID(context.changeset.id).tags(context.changeset.tags)); // Warnings
91122 body.call(commitWarnings); // Upload Explanation
91124 var saveSection = body.selectAll('.save-section').data([0]);
91125 saveSection = saveSection.enter().append('div').attr('class', 'modal-section save-section fillL').merge(saveSection);
91126 var prose = saveSection.selectAll('.commit-info').data([0]);
91128 if (prose.enter().size()) {
91129 // first time, make sure to update user details in prose
91130 _userDetails = null;
91133 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()
91134 // if needed, because it can trigger a style recalculation
91136 osm.userDetails(function (err, user) {
91138 if (_userDetails === user) return; // no change
91140 _userDetails = user;
91141 var userLink = select(document.createElement('div'));
91143 if (user.image_url) {
91144 userLink.append('img').attr('src', user.image_url).attr('class', 'icon pre-text user-icon');
91147 userLink.append('a').attr('class', 'user-info').html(user.display_name).attr('href', osm.userURL(user.display_name)).attr('target', '_blank');
91148 prose.html(_t.html('commit.upload_explanation_with_user', {
91149 user: userLink.html()
91151 }); // Request Review
91153 var requestReview = saveSection.selectAll('.request-review').data([0]); // Enter
91155 var requestReviewEnter = requestReview.enter().append('div').attr('class', 'request-review');
91156 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
91157 var labelEnter = requestReviewEnter.append('label').attr('for', requestReviewDomId);
91158 labelEnter.append('input').attr('type', 'checkbox').attr('id', requestReviewDomId);
91159 labelEnter.append('span').html(_t.html('commit.request_review')); // Update
91161 requestReview = requestReview.merge(requestReviewEnter);
91162 var requestReviewInput = requestReview.selectAll('input').property('checked', isReviewRequested(context.changeset.tags)).on('change', toggleRequestReview); // Buttons
91164 var buttonSection = saveSection.selectAll('.buttons').data([0]); // enter
91166 var buttonEnter = buttonSection.enter().append('div').attr('class', 'buttons fillL');
91167 buttonEnter.append('button').attr('class', 'secondary-action button cancel-button').append('span').attr('class', 'label').html(_t.html('commit.cancel'));
91168 var uploadButton = buttonEnter.append('button').attr('class', 'action button save-button');
91169 uploadButton.append('span').attr('class', 'label').html(_t.html('commit.save'));
91170 var uploadBlockerTooltipText = getUploadBlockerMessage(); // update
91172 buttonSection = buttonSection.merge(buttonEnter);
91173 buttonSection.selectAll('.cancel-button').on('click.cancel', function () {
91174 dispatch$1.call('cancel', this);
91176 buttonSection.selectAll('.save-button').classed('disabled', uploadBlockerTooltipText !== null).on('click.save', function () {
91177 if (!select(this).classed('disabled')) {
91178 this.blur(); // avoid keeping focus on the button - #4641
91180 for (var key in context.changeset.tags) {
91181 // remove any empty keys before upload
91182 if (!key) delete context.changeset.tags[key];
91185 context.uploader().save(context.changeset);
91187 }); // remove any existing tooltip
91189 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
91191 if (uploadBlockerTooltipText) {
91192 buttonSection.selectAll('.save-button').call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
91193 } // Raw Tag Editor
91196 var tagSection = body.selectAll('.tag-section.raw-tag-editor').data([0]);
91197 tagSection = tagSection.enter().append('div').attr('class', 'modal-section tag-section raw-tag-editor').merge(tagSection);
91198 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
91200 var changesSection = body.selectAll('.commit-changes-section').data([0]);
91201 changesSection = changesSection.enter().append('div').attr('class', 'modal-section commit-changes-section').merge(changesSection); // Change summary
91203 changesSection.call(commitChanges.render);
91205 function toggleRequestReview() {
91206 var rr = requestReviewInput.property('checked');
91208 review_requested: rr ? 'yes' : undefined
91210 tagSection.call(rawTagEditor.tags(Object.assign({}, context.changeset.tags)) // shallow copy
91215 function getUploadBlockerMessage() {
91216 var errors = context.validator().getIssuesBySeverity({
91221 if (errors.length) {
91222 return _t('commit.outstanding_errors_message', {
91223 count: errors.length
91226 var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
91228 if (!hasChangesetComment) {
91229 return _t('commit.comment_needed_message');
91236 function changeTags(_, changed, onInput) {
91237 if (changed.hasOwnProperty('comment')) {
91238 if (changed.comment === undefined) {
91239 changed.comment = '';
91243 corePreferences('comment', changed.comment);
91244 corePreferences('commentDate', Date.now());
91248 if (changed.hasOwnProperty('source')) {
91249 if (changed.source === undefined) {
91250 corePreferences('source', null);
91251 } else if (!onInput) {
91252 corePreferences('source', changed.source);
91253 corePreferences('commentDate', Date.now());
91255 } // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
91258 updateChangeset(changed, onInput);
91261 _selection.call(render);
91265 function findHashtags(tags, commentOnly) {
91266 var detectedHashtags = commentHashtags();
91268 if (detectedHashtags.length) {
91269 // always remove stored hashtags if there are hashtags in the comment - #4304
91270 corePreferences('hashtags', null);
91273 if (!detectedHashtags.length || !commentOnly) {
91274 detectedHashtags = detectedHashtags.concat(hashtagHashtags());
91277 var allLowerCase = new Set();
91278 return detectedHashtags.filter(function (hashtag) {
91279 // Compare tags as lowercase strings, but keep original case tags
91280 var lowerCase = hashtag.toLowerCase();
91282 if (!allLowerCase.has(lowerCase)) {
91283 allLowerCase.add(lowerCase);
91288 }); // Extract hashtags from `comment`
91290 function commentHashtags() {
91291 var matches = (tags.comment || '').replace(/http\S*/g, '') // drop anything that looks like a URL - #4289
91292 .match(hashtagRegex);
91293 return matches || [];
91294 } // Extract and clean hashtags from `hashtags`
91297 function hashtagHashtags() {
91298 var matches = (tags.hashtags || '').split(/[,;\s]+/).map(function (s) {
91299 if (s[0] !== '#') {
91304 var matched = s.match(hashtagRegex);
91305 return matched && matched[0];
91306 }).filter(Boolean); // exclude falsy
91308 return matches || [];
91312 function isReviewRequested(tags) {
91313 var rr = tags.review_requested;
91314 if (rr === undefined) return false;
91315 rr = rr.trim().toLowerCase();
91316 return !(rr === '' || rr === 'no');
91319 function updateChangeset(changed, onInput) {
91320 var tags = Object.assign({}, context.changeset.tags); // shallow copy
91322 Object.keys(changed).forEach(function (k) {
91323 var v = changed[k];
91324 k = context.cleanTagKey(k);
91325 if (readOnlyTags.indexOf(k) !== -1) return;
91327 if (v === undefined) {
91329 } else if (onInput) {
91332 tags[k] = context.cleanTagValue(v);
91337 // when changing the comment, override hashtags with any found in comment.
91338 var commentOnly = changed.hasOwnProperty('comment') && changed.comment !== '';
91339 var arr = findHashtags(tags, commentOnly);
91342 tags.hashtags = context.cleanTagValue(arr.join(';'));
91343 corePreferences('hashtags', tags.hashtags);
91345 delete tags.hashtags;
91346 corePreferences('hashtags', null);
91348 } // always update userdetails, just in case user reauthenticates as someone else
91351 if (_userDetails && _userDetails.changesets_count !== undefined) {
91352 var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1; // #4283
91354 tags.changesets_count = String(changesetsCount); // first 100 edits - new user
91356 if (changesetsCount <= 100) {
91358 s = corePreferences('walkthrough_completed');
91361 tags['ideditor:walkthrough_completed'] = s;
91364 s = corePreferences('walkthrough_progress');
91367 tags['ideditor:walkthrough_progress'] = s;
91370 s = corePreferences('walkthrough_started');
91373 tags['ideditor:walkthrough_started'] = s;
91377 delete tags.changesets_count;
91380 if (!fastDeepEqual(context.changeset.tags, tags)) {
91381 context.changeset = context.changeset.update({
91387 commit.reset = function () {
91388 context.changeset = null;
91391 return utilRebind(commit, dispatch$1, 'on');
91394 var globalIsFinite = global_1.isFinite;
91396 // `Number.isFinite` method
91397 // https://tc39.github.io/ecma262/#sec-number.isfinite
91398 var numberIsFinite = Number.isFinite || function isFinite(it) {
91399 return typeof it == 'number' && globalIsFinite(it);
91402 // `Number.isFinite` method
91403 // https://tc39.github.io/ecma262/#sec-number.isfinite
91404 _export({ target: 'Number', stat: true }, { isFinite: numberIsFinite });
91406 var RADIUS = 6378137;
91407 var FLATTENING = 1 / 298.257223563;
91408 var POLAR_RADIUS$1 = 6356752.3142;
91411 FLATTENING: FLATTENING,
91412 POLAR_RADIUS: POLAR_RADIUS$1
91415 var geometry_1 = geometry;
91416 var ring = ringArea;
91418 function geometry(_) {
91424 return polygonArea(_.coordinates);
91426 case 'MultiPolygon':
91427 for (i = 0; i < _.coordinates.length; i++) {
91428 area += polygonArea(_.coordinates[i]);
91436 case 'MultiLineString':
91439 case 'GeometryCollection':
91440 for (i = 0; i < _.geometries.length; i++) {
91441 area += geometry(_.geometries[i]);
91448 function polygonArea(coords) {
91451 if (coords && coords.length > 0) {
91452 area += Math.abs(ringArea(coords[0]));
91454 for (var i = 1; i < coords.length; i++) {
91455 area -= Math.abs(ringArea(coords[i]));
91462 * Calculate the approximate area of the polygon were it projected onto
91463 * the earth. Note that this area will be positive if ring is oriented
91464 * clockwise, otherwise it will be negative.
91467 * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
91468 * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
91469 * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
91472 * {float} The approximate signed geodesic area of the polygon in square
91477 function ringArea(coords) {
91486 coordsLength = coords.length;
91488 if (coordsLength > 2) {
91489 for (i = 0; i < coordsLength; i++) {
91490 if (i === coordsLength - 2) {
91492 lowerIndex = coordsLength - 2;
91493 middleIndex = coordsLength - 1;
91495 } else if (i === coordsLength - 1) {
91497 lowerIndex = coordsLength - 1;
91503 middleIndex = i + 1;
91504 upperIndex = i + 2;
91507 p1 = coords[lowerIndex];
91508 p2 = coords[middleIndex];
91509 p3 = coords[upperIndex];
91510 area += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]));
91513 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
91520 return _ * Math.PI / 180;
91523 var geojsonArea = {
91524 geometry: geometry_1,
91528 function toRadians(angleInDegrees) {
91529 return angleInDegrees * Math.PI / 180;
91532 function toDegrees(angleInRadians) {
91533 return angleInRadians * 180 / Math.PI;
91536 function offset(c1, distance, bearing) {
91537 var lat1 = toRadians(c1[1]);
91538 var lon1 = toRadians(c1[0]);
91539 var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
91541 var lat = Math.asin(Math.sin(lat1) * Math.cos(dByR) + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
91542 var lon = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
91543 return [toDegrees(lon), toDegrees(lat)];
91546 function validateCenter(center) {
91547 var validCenterLengths = [2, 3];
91549 if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
91550 throw new Error("ERROR! Center has to be an array of length two or three");
91553 var _center = _slicedToArray(center, 2),
91557 if (typeof lng !== "number" || typeof lat !== "number") {
91558 throw new Error("ERROR! Longitude and Latitude has to be numbers but where ".concat(_typeof(lng), " and ").concat(_typeof(lat)));
91561 if (lng > 180 || lng < -180) {
91562 throw new Error("ERROR! Longitude has to be between -180 and 180 but was ".concat(lng));
91565 if (lat > 90 || lat < -90) {
91566 throw new Error("ERROR! Latitude has to be between -90 and 90 but was ".concat(lat));
91570 function validateRadius(radius) {
91571 if (typeof radius !== "number") {
91572 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(_typeof(radius)));
91576 throw new Error("ERROR! Radius has to be a positive number but was: ".concat(radius));
91580 function validateNumberOfSegments(numberOfSegments) {
91581 if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
91582 throw new Error("ERROR! Number of segments has to be a number but was: ".concat(_typeof(numberOfSegments)));
91585 if (numberOfSegments < 3) {
91586 throw new Error("ERROR! Number of segments has to be at least 3 but was: ".concat(numberOfSegments));
91590 function validateInput(_ref) {
91591 var center = _ref.center,
91592 radius = _ref.radius,
91593 numberOfSegments = _ref.numberOfSegments;
91594 validateCenter(center);
91595 validateRadius(radius);
91596 validateNumberOfSegments(numberOfSegments);
91599 var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
91600 var n = numberOfSegments ? numberOfSegments : 32; // validateInput() throws error on invalid input and do nothing on valid input
91605 numberOfSegments: numberOfSegments
91607 var coordinates = [];
91609 for (var i = 0; i < n; ++i) {
91610 coordinates.push(offset(center, radius, 2 * Math.PI * -i / n));
91613 coordinates.push(coordinates[0]);
91616 coordinates: [coordinates]
91620 // `Number.EPSILON` constant
91621 // https://tc39.github.io/ecma262/#sec-number.epsilon
91622 _export({ target: 'Number', stat: true }, {
91623 EPSILON: Math.pow(2, -52)
91628 * Fast Splay tree for Node and browser
91630 * @author Alexander Milevski <info@w8r.name>
91634 var Node$1 = function Node(key, data) {
91635 _classCallCheck(this, Node);
91643 /* follows "An implementation of top-down splaying"
91644 * by D. Sleator <sleator@cs.cmu.edu> March 1992
91648 function DEFAULT_COMPARE$1(a, b) {
91649 return a > b ? 1 : a < b ? -1 : 0;
91652 * Simple top down splay, not requiring i to be in the tree t.
91656 function splay(i, t, comparator) {
91657 var N = new Node$1(null, null);
91662 var cmp = comparator(i, t.key); //if (i < t.key) {
91665 if (t.left === null) break; //if (i < t.left.key) {
91667 if (comparator(i, t.left.key) < 0) {
91674 if (t.left === null) break;
91681 t = t.left; //} else if (i > t.key) {
91682 } else if (cmp > 0) {
91683 if (t.right === null) break; //if (i > t.right.key) {
91685 if (comparator(i, t.right.key) > 0) {
91692 if (t.right === null) break;
91712 function _insert(i, data, t, comparator) {
91713 var node = new Node$1(i, data);
91716 node.left = node.right = null;
91720 t = splay(i, t, comparator);
91721 var cmp = comparator(i, t.key);
91724 node.left = t.left;
91727 } else if (cmp >= 0) {
91728 node.right = t.right;
91736 function _split(key, v, comparator) {
91741 v = splay(key, v, comparator);
91742 var cmp = comparator(v.key, key);
91747 } else if (cmp < 0) {
91764 function merge$4(left, right, comparator) {
91765 if (right === null) return left;
91766 if (left === null) return right;
91767 right = splay(left.key, right, comparator);
91772 * Prints level of the tree
91776 function printRow(root, prefix, isTail, out, printNode) {
91778 out("".concat(prefix).concat(isTail ? '└── ' : '├── ').concat(printNode(root), "\n"));
91779 var indent = prefix + (isTail ? ' ' : '│ ');
91780 if (root.left) printRow(root.left, indent, false, out, printNode);
91781 if (root.right) printRow(root.right, indent, true, out, printNode);
91785 var Tree = /*#__PURE__*/function () {
91787 var comparator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_COMPARE$1;
91789 _classCallCheck(this, Tree);
91793 this._comparator = comparator;
91796 * Inserts a key, allows duplicates
91800 _createClass(Tree, [{
91802 value: function insert(key, data) {
91804 return this._root = _insert(key, data, this._root, this._comparator);
91807 * Adds a key, if it is not present in the tree
91812 value: function add(key, data) {
91813 var node = new Node$1(key, data);
91815 if (this._root === null) {
91816 node.left = node.right = null;
91821 var comparator = this._comparator;
91822 var t = splay(key, this._root, comparator);
91823 var cmp = comparator(key, t.key);
91824 if (cmp === 0) this._root = t;else {
91826 node.left = t.left;
91829 } else if (cmp > 0) {
91830 node.right = t.right;
91842 * @return {Node|null}
91847 value: function remove(key) {
91848 this._root = this._remove(key, this._root, this._comparator);
91851 * Deletes i from the tree if it's there
91856 value: function _remove(i, t, comparator) {
91858 if (t === null) return null;
91859 t = splay(i, t, comparator);
91860 var cmp = comparator(i, t.key);
91864 if (t.left === null) {
91867 x = splay(i, t.left, comparator);
91876 /* It wasn't there */
91879 * Removes and returns the node with smallest key
91884 value: function pop() {
91885 var node = this._root;
91888 while (node.left) {
91892 this._root = splay(node.key, this._root, this._comparator);
91893 this._root = this._remove(node.key, this._root, this._comparator);
91903 * Find without splaying
91908 value: function findStatic(key) {
91909 var current = this._root;
91910 var compare = this._comparator;
91913 var cmp = compare(key, current.key);
91914 if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right;
91921 value: function find(key) {
91923 this._root = splay(key, this._root, this._comparator);
91924 if (this._comparator(key, this._root.key) !== 0) return null;
91931 value: function contains(key) {
91932 var current = this._root;
91933 var compare = this._comparator;
91936 var cmp = compare(key, current.key);
91937 if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right;
91944 value: function forEach(visitor, ctx) {
91945 var current = this._root;
91947 /* Initialize stack s */
91952 if (current !== null) {
91954 current = current.left;
91956 if (Q.length !== 0) {
91958 visitor.call(ctx, current);
91959 current = current.right;
91960 } else done = true;
91967 * Walk key range from `low` to `high`. Stops if `fn` returns a value.
91972 value: function range(low, high, fn, ctx) {
91974 var compare = this._comparator;
91975 var node = this._root;
91978 while (Q.length !== 0 || node) {
91984 cmp = compare(node.key, high);
91988 } else if (compare(node.key, low) >= 0) {
91989 if (fn.call(ctx, node)) return this; // stop if smth is returned
91999 * Returns array of keys
92004 value: function keys() {
92006 this.forEach(function (_ref) {
92007 var key = _ref.key;
92008 return keys.push(key);
92013 * Returns array of all the data in the nodes
92018 value: function values() {
92020 this.forEach(function (_ref2) {
92021 var data = _ref2.data;
92022 return values.push(data);
92028 value: function min() {
92029 if (this._root) return this.minNode(this._root).key;
92034 value: function max() {
92035 if (this._root) return this.maxNode(this._root).key;
92040 value: function minNode() {
92041 var t = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
92042 if (t) while (t.left) {
92049 value: function maxNode() {
92050 var t = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._root;
92051 if (t) while (t.right) {
92057 * Returns node at given index
92062 value: function at(index) {
92063 var current = this._root;
92071 current = current.left;
92073 if (Q.length > 0) {
92075 if (i === index) return current;
92077 current = current.right;
92078 } else done = true;
92086 value: function next(d) {
92087 var root = this._root;
92088 var successor = null;
92091 successor = d.right;
92093 while (successor.left) {
92094 successor = successor.left;
92100 var comparator = this._comparator;
92103 var cmp = comparator(d.key, root.key);
92104 if (cmp === 0) break;else if (cmp < 0) {
92107 } else root = root.right;
92114 value: function prev(d) {
92115 var root = this._root;
92116 var predecessor = null;
92118 if (d.left !== null) {
92119 predecessor = d.left;
92121 while (predecessor.right) {
92122 predecessor = predecessor.right;
92125 return predecessor;
92128 var comparator = this._comparator;
92131 var cmp = comparator(d.key, root.key);
92132 if (cmp === 0) break;else if (cmp < 0) root = root.left;else {
92133 predecessor = root;
92138 return predecessor;
92142 value: function clear() {
92149 value: function toList() {
92150 return _toList(this._root);
92153 * Bulk-load items. Both array have to be same size
92158 value: function load(keys) {
92159 var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
92160 var presort = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
92161 var size = keys.length;
92162 var comparator = this._comparator; // sort if needed
92164 if (presort) sort$1(keys, values, 0, size - 1, comparator);
92166 if (this._root === null) {
92168 this._root = loadRecursive$1(keys, values, 0, size);
92171 // that re-builds the whole tree from two in-order traversals
92172 var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
92173 size = this._size + size;
92174 this._root = sortedListToBST({
92183 value: function isEmpty() {
92184 return this._root === null;
92188 value: function toString() {
92189 var printNode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function (n) {
92190 return String(n.key);
92193 printRow(this._root, '', true, function (v) {
92194 return out.push(v);
92196 return out.join('');
92200 value: function update(key, newKey, newData) {
92201 var comparator = this._comparator;
92203 var _split2 = _split(key, this._root, comparator),
92204 left = _split2.left,
92205 right = _split2.right;
92207 if (comparator(key, newKey) < 0) {
92208 right = _insert(newKey, newData, right, comparator);
92210 left = _insert(newKey, newData, left, comparator);
92213 this._root = merge$4(left, right, comparator);
92217 value: function split(key) {
92218 return _split(key, this._root, this._comparator);
92222 get: function get() {
92227 get: function get() {
92235 function loadRecursive$1(keys, values, start, end) {
92236 var size = end - start;
92239 var middle = start + Math.floor(size / 2);
92240 var key = keys[middle];
92241 var data = values[middle];
92242 var node = new Node$1(key, data);
92243 node.left = loadRecursive$1(keys, values, start, middle);
92244 node.right = loadRecursive$1(keys, values, middle + 1, end);
92251 function createList(keys, values) {
92252 var head = new Node$1(null, null);
92255 for (var i = 0; i < keys.length; i++) {
92256 p = p.next = new Node$1(keys[i], values[i]);
92263 function _toList(root) {
92264 var current = root;
92267 var head = new Node$1(null, null);
92273 current = current.left;
92275 if (Q.length > 0) {
92276 current = p = p.next = Q.pop();
92277 current = current.right;
92278 } else done = true;
92282 p.next = null; // that'll work even if the tree was empty
92287 function sortedListToBST(list, start, end) {
92288 var size = end - start;
92291 var middle = start + Math.floor(size / 2);
92292 var left = sortedListToBST(list, start, middle);
92293 var root = list.head;
92295 list.head = list.head.next;
92296 root.right = sortedListToBST(list, middle + 1, end);
92303 function mergeLists(l1, l2, compare) {
92304 var head = new Node$1(null, null); // dummy
92310 while (p1 !== null && p2 !== null) {
92311 if (compare(p1.key, p2.key) < 0) {
92324 } else if (p2 !== null) {
92331 function sort$1(keys, values, left, right, compare) {
92332 if (left >= right) return;
92333 var pivot = keys[left + right >> 1];
92340 } while (compare(keys[i], pivot) < 0);
92344 } while (compare(keys[j], pivot) > 0);
92351 values[i] = values[j];
92355 sort$1(keys, values, left, j, compare);
92356 sort$1(keys, values, j + 1, right, compare);
92359 function _classCallCheck$1(instance, Constructor) {
92360 if (!(instance instanceof Constructor)) {
92361 throw new TypeError("Cannot call a class as a function");
92365 function _defineProperties$1(target, props) {
92366 for (var i = 0; i < props.length; i++) {
92367 var descriptor = props[i];
92368 descriptor.enumerable = descriptor.enumerable || false;
92369 descriptor.configurable = true;
92370 if ("value" in descriptor) descriptor.writable = true;
92371 Object.defineProperty(target, descriptor.key, descriptor);
92375 function _createClass$1(Constructor, protoProps, staticProps) {
92376 if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
92377 if (staticProps) _defineProperties$1(Constructor, staticProps);
92378 return Constructor;
92381 * A bounding box has the format:
92383 * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
92388 var isInBbox = function isInBbox(bbox, point) {
92389 return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
92391 /* Returns either null, or a bbox (aka an ordered pair of points)
92392 * If there is only one point of overlap, a bbox with identical points
92393 * will be returned */
92396 var getBboxOverlap = function getBboxOverlap(b1, b2) {
92397 // check if the bboxes overlap at all
92398 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
92400 var lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
92401 var upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x; // find the middle two Y values
92403 var lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
92404 var upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y; // put those middle values together to get the overlap
92417 /* Javascript doesn't do integer math. Everything is
92418 * floating point with percision Number.EPSILON.
92420 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
92424 var epsilon$2 = Number.EPSILON; // IE Polyfill
92426 if (epsilon$2 === undefined) epsilon$2 = Math.pow(2, -52);
92427 var EPSILON_SQ = epsilon$2 * epsilon$2;
92428 /* FLP comparator */
92430 var cmp = function cmp(a, b) {
92431 // check if they're both 0
92432 if (-epsilon$2 < a && a < epsilon$2) {
92433 if (-epsilon$2 < b && b < epsilon$2) {
92436 } // check if they're flp equal
92441 if (ab * ab < EPSILON_SQ * a * b) {
92443 } // normal comparison
92446 return a < b ? -1 : 1;
92449 * This class rounds incoming values sufficiently so that
92450 * floating points problems are, for the most part, avoided.
92452 * Incoming points are have their x & y values tested against
92453 * all previously seen x & y values. If either is 'too close'
92454 * to a previously seen value, it's value is 'snapped' to the
92455 * previously seen value.
92457 * All points should be rounded by this class before being
92458 * stored in any data structures in the rest of this algorithm.
92462 var PtRounder = /*#__PURE__*/function () {
92463 function PtRounder() {
92464 _classCallCheck$1(this, PtRounder);
92469 _createClass$1(PtRounder, [{
92471 value: function reset() {
92472 this.xRounder = new CoordRounder();
92473 this.yRounder = new CoordRounder();
92477 value: function round(x, y) {
92479 x: this.xRounder.round(x),
92480 y: this.yRounder.round(y)
92488 var CoordRounder = /*#__PURE__*/function () {
92489 function CoordRounder() {
92490 _classCallCheck$1(this, CoordRounder);
92492 this.tree = new Tree(); // preseed with 0 so we don't end up with values < Number.EPSILON
92495 } // Note: this can rounds input values backwards or forwards.
92496 // You might ask, why not restrict this to just rounding
92497 // forwards? Wouldn't that allow left endpoints to always
92498 // remain left endpoints during splitting (never change to
92499 // right). No - it wouldn't, because we snap intersections
92500 // to endpoints (to establish independence from the segment
92501 // angle for t-intersections).
92504 _createClass$1(CoordRounder, [{
92506 value: function round(coord) {
92507 var node = this.tree.add(coord);
92508 var prevNode = this.tree.prev(node);
92510 if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
92511 this.tree.remove(coord);
92512 return prevNode.key;
92515 var nextNode = this.tree.next(node);
92517 if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
92518 this.tree.remove(coord);
92519 return nextNode.key;
92526 return CoordRounder;
92527 }(); // singleton available by import
92530 var rounder = new PtRounder();
92531 /* Cross Product of two vectors with first point at origin */
92533 var crossProduct$1 = function crossProduct(a, b) {
92534 return a.x * b.y - a.y * b.x;
92536 /* Dot Product of two vectors with first point at origin */
92539 var dotProduct$1 = function dotProduct(a, b) {
92540 return a.x * b.x + a.y * b.y;
92542 /* Comparator for two vectors with same starting point */
92545 var compareVectorAngles = function compareVectorAngles(basePt, endPt1, endPt2) {
92547 x: endPt1.x - basePt.x,
92548 y: endPt1.y - basePt.y
92551 x: endPt2.x - basePt.x,
92552 y: endPt2.y - basePt.y
92554 var kross = crossProduct$1(v1, v2);
92555 return cmp(kross, 0);
92558 var length = function length(v) {
92559 return Math.sqrt(dotProduct$1(v, v));
92561 /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */
92564 var sineOfAngle = function sineOfAngle(pShared, pBase, pAngle) {
92566 x: pBase.x - pShared.x,
92567 y: pBase.y - pShared.y
92570 x: pAngle.x - pShared.x,
92571 y: pAngle.y - pShared.y
92573 return crossProduct$1(vAngle, vBase) / length(vAngle) / length(vBase);
92575 /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */
92578 var cosineOfAngle = function cosineOfAngle(pShared, pBase, pAngle) {
92580 x: pBase.x - pShared.x,
92581 y: pBase.y - pShared.y
92584 x: pAngle.x - pShared.x,
92585 y: pAngle.y - pShared.y
92587 return dotProduct$1(vAngle, vBase) / length(vAngle) / length(vBase);
92589 /* Get the x coordinate where the given line (defined by a point and vector)
92590 * crosses the horizontal line with the given y coordiante.
92591 * In the case of parrallel lines (including overlapping ones) returns null. */
92594 var horizontalIntersection = function horizontalIntersection(pt, v, y) {
92595 if (v.y === 0) return null;
92597 x: pt.x + v.x / v.y * (y - pt.y),
92601 /* Get the y coordinate where the given line (defined by a point and vector)
92602 * crosses the vertical line with the given x coordiante.
92603 * In the case of parrallel lines (including overlapping ones) returns null. */
92606 var verticalIntersection = function verticalIntersection(pt, v, x) {
92607 if (v.x === 0) return null;
92610 y: pt.y + v.y / v.x * (x - pt.x)
92613 /* Get the intersection of two lines, each defined by a base point and a vector.
92614 * In the case of parrallel lines (including overlapping ones) returns null. */
92617 var intersection$1 = function intersection(pt1, v1, pt2, v2) {
92618 // take some shortcuts for vertical and horizontal lines
92619 // this also ensures we don't calculate an intersection and then discover
92620 // it's actually outside the bounding box of the line
92621 if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
92622 if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
92623 if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
92624 if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y); // General case for non-overlapping segments.
92625 // This algorithm is based on Schneider and Eberly.
92626 // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244
92628 var kross = crossProduct$1(v1, v2);
92629 if (kross == 0) return null;
92634 var d1 = crossProduct$1(ve, v1) / kross;
92635 var d2 = crossProduct$1(ve, v2) / kross; // take the average of the two calculations to minimize rounding error
92637 var x1 = pt1.x + d2 * v1.x,
92638 x2 = pt2.x + d1 * v2.x;
92639 var y1 = pt1.y + d2 * v1.y,
92640 y2 = pt2.y + d1 * v2.y;
92641 var x = (x1 + x2) / 2;
92642 var y = (y1 + y2) / 2;
92649 var SweepEvent$1 = /*#__PURE__*/function () {
92650 _createClass$1(SweepEvent, null, [{
92652 // for ordering sweep events in the sweep event queue
92653 value: function compare(a, b) {
92654 // favor event with a point that the sweep line hits first
92655 var ptCmp = SweepEvent.comparePoints(a.point, b.point);
92656 if (ptCmp !== 0) return ptCmp; // the points are the same, so link them if needed
92658 if (a.point !== b.point) a.link(b); // favor right events over left
92660 if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1; // we have two matching left or right endpoints
92661 // ordering of this case is the same as for their segments
92663 return Segment.compare(a.segment, b.segment);
92664 } // for ordering points in sweep line order
92667 key: "comparePoints",
92668 value: function comparePoints(aPt, bPt) {
92669 if (aPt.x < bPt.x) return -1;
92670 if (aPt.x > bPt.x) return 1;
92671 if (aPt.y < bPt.y) return -1;
92672 if (aPt.y > bPt.y) return 1;
92674 } // Warning: 'point' input will be modified and re-used (for performance)
92678 function SweepEvent(point, isLeft) {
92679 _classCallCheck$1(this, SweepEvent);
92681 if (point.events === undefined) point.events = [this];else point.events.push(this);
92682 this.point = point;
92683 this.isLeft = isLeft; // this.segment, this.otherSE set by factory
92686 _createClass$1(SweepEvent, [{
92688 value: function link(other) {
92689 if (other.point === this.point) {
92690 throw new Error('Tried to link already linked events');
92693 var otherEvents = other.point.events;
92695 for (var i = 0, iMax = otherEvents.length; i < iMax; i++) {
92696 var evt = otherEvents[i];
92697 this.point.events.push(evt);
92698 evt.point = this.point;
92701 this.checkForConsuming();
92703 /* Do a pass over our linked events and check to see if any pair
92704 * of segments match, and should be consumed. */
92707 key: "checkForConsuming",
92708 value: function checkForConsuming() {
92709 // FIXME: The loops in this method run O(n^2) => no good.
92710 // Maintain little ordered sweep event trees?
92711 // Can we maintaining an ordering that avoids the need
92712 // for the re-sorting with getLeftmostComparator in geom-out?
92713 // Compare each pair of events to see if other events also match
92714 var numEvents = this.point.events.length;
92716 for (var i = 0; i < numEvents; i++) {
92717 var evt1 = this.point.events[i];
92718 if (evt1.segment.consumedBy !== undefined) continue;
92720 for (var j = i + 1; j < numEvents; j++) {
92721 var evt2 = this.point.events[j];
92722 if (evt2.consumedBy !== undefined) continue;
92723 if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
92724 evt1.segment.consume(evt2.segment);
92729 key: "getAvailableLinkedEvents",
92730 value: function getAvailableLinkedEvents() {
92731 // point.events is always of length 2 or greater
92734 for (var i = 0, iMax = this.point.events.length; i < iMax; i++) {
92735 var evt = this.point.events[i];
92737 if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
92745 * Returns a comparator function for sorting linked events that will
92746 * favor the event that will give us the smallest left-side angle.
92747 * All ring construction starts as low as possible heading to the right,
92748 * so by always turning left as sharp as possible we'll get polygons
92749 * without uncessary loops & holes.
92751 * The comparator function has a compute cache such that it avoids
92752 * re-computing already-computed values.
92756 key: "getLeftmostComparator",
92757 value: function getLeftmostComparator(baseEvent) {
92760 var cache = new Map();
92762 var fillCache = function fillCache(linkedEvent) {
92763 var nextEvent = linkedEvent.otherSE;
92764 cache.set(linkedEvent, {
92765 sine: sineOfAngle(_this.point, baseEvent.point, nextEvent.point),
92766 cosine: cosineOfAngle(_this.point, baseEvent.point, nextEvent.point)
92770 return function (a, b) {
92771 if (!cache.has(a)) fillCache(a);
92772 if (!cache.has(b)) fillCache(b);
92774 var _cache$get = cache.get(a),
92775 asine = _cache$get.sine,
92776 acosine = _cache$get.cosine;
92778 var _cache$get2 = cache.get(b),
92779 bsine = _cache$get2.sine,
92780 bcosine = _cache$get2.cosine; // both on or above x-axis
92783 if (asine >= 0 && bsine >= 0) {
92784 if (acosine < bcosine) return 1;
92785 if (acosine > bcosine) return -1;
92787 } // both below x-axis
92790 if (asine < 0 && bsine < 0) {
92791 if (acosine < bcosine) return -1;
92792 if (acosine > bcosine) return 1;
92794 } // one above x-axis, one below
92797 if (bsine < asine) return -1;
92798 if (bsine > asine) return 1;
92805 }(); // segments and sweep events when all else is identical
92810 var Segment = /*#__PURE__*/function () {
92811 _createClass$1(Segment, null, [{
92814 /* This compare() function is for ordering segments in the sweep
92815 * line tree, and does so according to the following criteria:
92817 * Consider the vertical line that lies an infinestimal step to the
92818 * right of the right-more of the two left endpoints of the input
92819 * segments. Imagine slowly moving a point up from negative infinity
92820 * in the increasing y direction. Which of the two segments will that
92821 * point intersect first? That segment comes 'before' the other one.
92823 * If neither segment would be intersected by such a line, (if one
92824 * or more of the segments are vertical) then the line to be considered
92825 * is directly on the right-more of the two left inputs.
92827 value: function compare(a, b) {
92828 var alx = a.leftSE.point.x;
92829 var blx = b.leftSE.point.x;
92830 var arx = a.rightSE.point.x;
92831 var brx = b.rightSE.point.x; // check if they're even in the same vertical plane
92833 if (brx < alx) return 1;
92834 if (arx < blx) return -1;
92835 var aly = a.leftSE.point.y;
92836 var bly = b.leftSE.point.y;
92837 var ary = a.rightSE.point.y;
92838 var bry = b.rightSE.point.y; // is left endpoint of segment B the right-more?
92841 // are the two segments in the same horizontal plane?
92842 if (bly < aly && bly < ary) return 1;
92843 if (bly > aly && bly > ary) return -1; // is the B left endpoint colinear to segment A?
92845 var aCmpBLeft = a.comparePoint(b.leftSE.point);
92846 if (aCmpBLeft < 0) return 1;
92847 if (aCmpBLeft > 0) return -1; // is the A right endpoint colinear to segment B ?
92849 var bCmpARight = b.comparePoint(a.rightSE.point);
92850 if (bCmpARight !== 0) return bCmpARight; // colinear segments, consider the one with left-more
92851 // left endpoint to be first (arbitrary?)
92854 } // is left endpoint of segment A the right-more?
92858 if (aly < bly && aly < bry) return -1;
92859 if (aly > bly && aly > bry) return 1; // is the A left endpoint colinear to segment B?
92861 var bCmpALeft = b.comparePoint(a.leftSE.point);
92862 if (bCmpALeft !== 0) return bCmpALeft; // is the B right endpoint colinear to segment A?
92864 var aCmpBRight = a.comparePoint(b.rightSE.point);
92865 if (aCmpBRight < 0) return 1;
92866 if (aCmpBRight > 0) return -1; // colinear segments, consider the one with left-more
92867 // left endpoint to be first (arbitrary?)
92870 } // if we get here, the two left endpoints are in the same
92871 // vertical plane, ie alx === blx
92872 // consider the lower left-endpoint to come first
92875 if (aly < bly) return -1;
92876 if (aly > bly) return 1; // left endpoints are identical
92877 // check for colinearity by using the left-more right endpoint
92878 // is the A right endpoint more left-more?
92881 var _bCmpARight = b.comparePoint(a.rightSE.point);
92883 if (_bCmpARight !== 0) return _bCmpARight;
92884 } // is the B right endpoint more left-more?
92888 var _aCmpBRight = a.comparePoint(b.rightSE.point);
92890 if (_aCmpBRight < 0) return 1;
92891 if (_aCmpBRight > 0) return -1;
92895 // are these two [almost] vertical segments with opposite orientation?
92896 // if so, the one with the lower right endpoint comes first
92897 var ay = ary - aly;
92898 var ax = arx - alx;
92899 var by = bry - bly;
92900 var bx = brx - blx;
92901 if (ay > ax && by < bx) return 1;
92902 if (ay < ax && by > bx) return -1;
92903 } // we have colinear segments with matching orientation
92904 // consider the one with more left-more right endpoint to be first
92907 if (arx > brx) return 1;
92908 if (arx < brx) return -1; // if we get here, two two right endpoints are in the same
92909 // vertical plane, ie arx === brx
92910 // consider the lower right-endpoint to come first
92912 if (ary < bry) return -1;
92913 if (ary > bry) return 1; // right endpoints identical as well, so the segments are idential
92914 // fall back on creation order as consistent tie-breaker
92916 if (a.id < b.id) return -1;
92917 if (a.id > b.id) return 1; // identical segment, ie a === b
92921 /* Warning: a reference to ringWindings input will be stored,
92922 * and possibly will be later modified */
92926 function Segment(leftSE, rightSE, rings, windings) {
92927 _classCallCheck$1(this, Segment);
92929 this.id = ++segmentId;
92930 this.leftSE = leftSE;
92931 leftSE.segment = this;
92932 leftSE.otherSE = rightSE;
92933 this.rightSE = rightSE;
92934 rightSE.segment = this;
92935 rightSE.otherSE = leftSE;
92936 this.rings = rings;
92937 this.windings = windings; // left unset for performance, set later in algorithm
92938 // this.ringOut, this.consumedBy, this.prev
92941 _createClass$1(Segment, [{
92942 key: "replaceRightSE",
92944 /* When a segment is split, the rightSE is replaced with a new sweep event */
92945 value: function replaceRightSE(newRightSE) {
92946 this.rightSE = newRightSE;
92947 this.rightSE.segment = this;
92948 this.rightSE.otherSE = this.leftSE;
92949 this.leftSE.otherSE = this.rightSE;
92953 value: function bbox() {
92954 var y1 = this.leftSE.point.y;
92955 var y2 = this.rightSE.point.y;
92958 x: this.leftSE.point.x,
92959 y: y1 < y2 ? y1 : y2
92962 x: this.rightSE.point.x,
92963 y: y1 > y2 ? y1 : y2
92967 /* A vector from the left point to the right */
92971 value: function vector() {
92973 x: this.rightSE.point.x - this.leftSE.point.x,
92974 y: this.rightSE.point.y - this.leftSE.point.y
92978 key: "isAnEndpoint",
92979 value: function isAnEndpoint(pt) {
92980 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;
92982 /* Compare this segment with a point.
92984 * A point P is considered to be colinear to a segment if there
92985 * exists a distance D such that if we travel along the segment
92986 * from one * endpoint towards the other a distance D, we find
92987 * ourselves at point P.
92989 * Return value indicates:
92991 * 1: point lies above the segment (to the left of vertical)
92992 * 0: point is colinear to segment
92993 * -1: point lies below the segment (to the right of vertical)
92997 key: "comparePoint",
92998 value: function comparePoint(point) {
92999 if (this.isAnEndpoint(point)) return 0;
93000 var lPt = this.leftSE.point;
93001 var rPt = this.rightSE.point;
93002 var v = this.vector(); // Exactly vertical segments.
93004 if (lPt.x === rPt.x) {
93005 if (point.x === lPt.x) return 0;
93006 return point.x < lPt.x ? 1 : -1;
93007 } // Nearly vertical segments with an intersection.
93008 // Check to see where a point on the line with matching Y coordinate is.
93011 var yDist = (point.y - lPt.y) / v.y;
93012 var xFromYDist = lPt.x + yDist * v.x;
93013 if (point.x === xFromYDist) return 0; // General case.
93014 // Check to see where a point on the line with matching X coordinate is.
93016 var xDist = (point.x - lPt.x) / v.x;
93017 var yFromXDist = lPt.y + xDist * v.y;
93018 if (point.y === yFromXDist) return 0;
93019 return point.y < yFromXDist ? -1 : 1;
93022 * Given another segment, returns the first non-trivial intersection
93023 * between the two segments (in terms of sweep line ordering), if it exists.
93025 * A 'non-trivial' intersection is one that will cause one or both of the
93026 * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
93028 * * endpoint of segA with endpoint of segB --> trivial
93029 * * endpoint of segA with point along segB --> non-trivial
93030 * * endpoint of segB with point along segA --> non-trivial
93031 * * point along segA with point along segB --> non-trivial
93033 * If no non-trivial intersection exists, return null
93034 * Else, return null.
93038 key: "getIntersection",
93039 value: function getIntersection(other) {
93040 // If bboxes don't overlap, there can't be any intersections
93041 var tBbox = this.bbox();
93042 var oBbox = other.bbox();
93043 var bboxOverlap = getBboxOverlap(tBbox, oBbox);
93044 if (bboxOverlap === null) return null; // We first check to see if the endpoints can be considered intersections.
93045 // This will 'snap' intersections to endpoints if possible, and will
93046 // handle cases of colinearity.
93048 var tlp = this.leftSE.point;
93049 var trp = this.rightSE.point;
93050 var olp = other.leftSE.point;
93051 var orp = other.rightSE.point; // does each endpoint touch the other segment?
93052 // note that we restrict the 'touching' definition to only allow segments
93053 // to touch endpoints that lie forward from where we are in the sweep line pass
93055 var touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
93056 var touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
93057 var touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
93058 var touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0; // do left endpoints match?
93060 if (touchesThisLSE && touchesOtherLSE) {
93061 // these two cases are for colinear segments with matching left
93062 // endpoints, and one segment being longer than the other
93063 if (touchesThisRSE && !touchesOtherRSE) return trp;
93064 if (!touchesThisRSE && touchesOtherRSE) return orp; // either the two segments match exactly (two trival intersections)
93065 // or just on their left endpoint (one trivial intersection
93068 } // does this left endpoint matches (other doesn't)
93071 if (touchesThisLSE) {
93072 // check for segments that just intersect on opposing endpoints
93073 if (touchesOtherRSE) {
93074 if (tlp.x === orp.x && tlp.y === orp.y) return null;
93075 } // t-intersection on left endpoint
93079 } // does other left endpoint matches (this doesn't)
93082 if (touchesOtherLSE) {
93083 // check for segments that just intersect on opposing endpoints
93084 if (touchesThisRSE) {
93085 if (trp.x === olp.x && trp.y === olp.y) return null;
93086 } // t-intersection on left endpoint
93090 } // trivial intersection on right endpoints
93093 if (touchesThisRSE && touchesOtherRSE) return null; // t-intersections on just one right endpoint
93095 if (touchesThisRSE) return trp;
93096 if (touchesOtherRSE) return orp; // None of our endpoints intersect. Look for a general intersection between
93097 // infinite lines laid over the segments
93099 var pt = intersection$1(tlp, this.vector(), olp, other.vector()); // are the segments parrallel? Note that if they were colinear with overlap,
93100 // they would have an endpoint intersection and that case was already handled above
93102 if (pt === null) return null; // is the intersection found between the lines not on the segments?
93104 if (!isInBbox(bboxOverlap, pt)) return null; // round the the computed point if needed
93106 return rounder.round(pt.x, pt.y);
93109 * Split the given segment into multiple segments on the given points.
93110 * * Each existing segment will retain its leftSE and a new rightSE will be
93111 * generated for it.
93112 * * A new segment will be generated which will adopt the original segment's
93113 * rightSE, and a new leftSE will be generated for it.
93114 * * If there are more than two points given to split on, new segments
93115 * in the middle will be generated with new leftSE and rightSE's.
93116 * * An array of the newly generated SweepEvents will be returned.
93118 * Warning: input array of points is modified
93123 value: function split(point) {
93124 var newEvents = [];
93125 var alreadyLinked = point.events !== undefined;
93126 var newLeftSE = new SweepEvent$1(point, true);
93127 var newRightSE = new SweepEvent$1(point, false);
93128 var oldRightSE = this.rightSE;
93129 this.replaceRightSE(newRightSE);
93130 newEvents.push(newRightSE);
93131 newEvents.push(newLeftSE);
93132 var newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice()); // when splitting a nearly vertical downward-facing segment,
93133 // sometimes one of the resulting new segments is vertical, in which
93134 // case its left and right events may need to be swapped
93136 if (SweepEvent$1.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
93137 newSeg.swapEvents();
93140 if (SweepEvent$1.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
93142 } // in the point we just used to create new sweep events with was already
93143 // linked to other events, we need to check if either of the affected
93144 // segments should be consumed
93147 if (alreadyLinked) {
93148 newLeftSE.checkForConsuming();
93149 newRightSE.checkForConsuming();
93154 /* Swap which event is left and right */
93158 value: function swapEvents() {
93159 var tmpEvt = this.rightSE;
93160 this.rightSE = this.leftSE;
93161 this.leftSE = tmpEvt;
93162 this.leftSE.isLeft = true;
93163 this.rightSE.isLeft = false;
93165 for (var i = 0, iMax = this.windings.length; i < iMax; i++) {
93166 this.windings[i] *= -1;
93169 /* Consume another segment. We take their rings under our wing
93170 * and mark them as consumed. Use for perfectly overlapping segments */
93174 value: function consume(other) {
93175 var consumer = this;
93176 var consumee = other;
93178 while (consumer.consumedBy) {
93179 consumer = consumer.consumedBy;
93182 while (consumee.consumedBy) {
93183 consumee = consumee.consumedBy;
93186 var cmp = Segment.compare(consumer, consumee);
93187 if (cmp === 0) return; // already consumed
93188 // the winner of the consumption is the earlier segment
93189 // according to sweep line ordering
93192 var tmp = consumer;
93193 consumer = consumee;
93195 } // make sure a segment doesn't consume it's prev
93198 if (consumer.prev === consumee) {
93199 var _tmp = consumer;
93200 consumer = consumee;
93204 for (var i = 0, iMax = consumee.rings.length; i < iMax; i++) {
93205 var ring = consumee.rings[i];
93206 var winding = consumee.windings[i];
93207 var index = consumer.rings.indexOf(ring);
93209 if (index === -1) {
93210 consumer.rings.push(ring);
93211 consumer.windings.push(winding);
93212 } else consumer.windings[index] += winding;
93215 consumee.rings = null;
93216 consumee.windings = null;
93217 consumee.consumedBy = consumer; // mark sweep events consumed as to maintain ordering in sweep event queue
93219 consumee.leftSE.consumedBy = consumer.leftSE;
93220 consumee.rightSE.consumedBy = consumer.rightSE;
93222 /* The first segment previous segment chain that is in the result */
93225 key: "prevInResult",
93226 value: function prevInResult() {
93227 if (this._prevInResult !== undefined) return this._prevInResult;
93228 if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult();
93229 return this._prevInResult;
93232 key: "beforeState",
93233 value: function beforeState() {
93234 if (this._beforeState !== undefined) return this._beforeState;
93235 if (!this.prev) this._beforeState = {
93240 var seg = this.prev.consumedBy || this.prev;
93241 this._beforeState = seg.afterState();
93243 return this._beforeState;
93247 value: function afterState() {
93248 if (this._afterState !== undefined) return this._afterState;
93249 var beforeState = this.beforeState();
93250 this._afterState = {
93251 rings: beforeState.rings.slice(0),
93252 windings: beforeState.windings.slice(0),
93255 var ringsAfter = this._afterState.rings;
93256 var windingsAfter = this._afterState.windings;
93257 var mpsAfter = this._afterState.multiPolys; // calculate ringsAfter, windingsAfter
93259 for (var i = 0, iMax = this.rings.length; i < iMax; i++) {
93260 var ring = this.rings[i];
93261 var winding = this.windings[i];
93262 var index = ringsAfter.indexOf(ring);
93264 if (index === -1) {
93265 ringsAfter.push(ring);
93266 windingsAfter.push(winding);
93267 } else windingsAfter[index] += winding;
93268 } // calcualte polysAfter
93271 var polysAfter = [];
93272 var polysExclude = [];
93274 for (var _i = 0, _iMax = ringsAfter.length; _i < _iMax; _i++) {
93275 if (windingsAfter[_i] === 0) continue; // non-zero rule
93277 var _ring = ringsAfter[_i];
93278 var poly = _ring.poly;
93279 if (polysExclude.indexOf(poly) !== -1) continue;
93280 if (_ring.isExterior) polysAfter.push(poly);else {
93281 if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
93283 var _index = polysAfter.indexOf(_ring.poly);
93285 if (_index !== -1) polysAfter.splice(_index, 1);
93287 } // calculate multiPolysAfter
93290 for (var _i2 = 0, _iMax2 = polysAfter.length; _i2 < _iMax2; _i2++) {
93291 var mp = polysAfter[_i2].multiPoly;
93292 if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
93295 return this._afterState;
93297 /* Is this segment part of the final result? */
93301 value: function isInResult() {
93302 // if we've been consumed, we're not in the result
93303 if (this.consumedBy) return false;
93304 if (this._isInResult !== undefined) return this._isInResult;
93305 var mpsBefore = this.beforeState().multiPolys;
93306 var mpsAfter = this.afterState().multiPolys;
93308 switch (operation.type) {
93311 // UNION - included iff:
93312 // * On one side of us there is 0 poly interiors AND
93313 // * On the other side there is 1 or more.
93314 var noBefores = mpsBefore.length === 0;
93315 var noAfters = mpsAfter.length === 0;
93316 this._isInResult = noBefores !== noAfters;
93320 case 'intersection':
93322 // INTERSECTION - included iff:
93323 // * on one side of us all multipolys are rep. with poly interiors AND
93324 // * on the other side of us, not all multipolys are repsented
93325 // with poly interiors
93329 if (mpsBefore.length < mpsAfter.length) {
93330 least = mpsBefore.length;
93331 most = mpsAfter.length;
93333 least = mpsAfter.length;
93334 most = mpsBefore.length;
93337 this._isInResult = most === operation.numMultiPolys && least < most;
93343 // XOR - included iff:
93344 // * the difference between the number of multipolys represented
93345 // with poly interiors on our two sides is an odd number
93346 var diff = Math.abs(mpsBefore.length - mpsAfter.length);
93347 this._isInResult = diff % 2 === 1;
93353 // DIFFERENCE included iff:
93354 // * on exactly one side, we have just the subject
93355 var isJustSubject = function isJustSubject(mps) {
93356 return mps.length === 1 && mps[0].isSubject;
93359 this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
93364 throw new Error("Unrecognized operation type found ".concat(operation.type));
93367 return this._isInResult;
93371 value: function fromRing(pt1, pt2, ring) {
93372 var leftPt, rightPt, winding; // ordering the two points according to sweep line ordering
93374 var cmpPts = SweepEvent$1.comparePoints(pt1, pt2);
93380 } else if (cmpPts > 0) {
93384 } else throw new Error("Tried to create degenerate segment at [".concat(pt1.x, ", ").concat(pt1.y, "]"));
93386 var leftSE = new SweepEvent$1(leftPt, true);
93387 var rightSE = new SweepEvent$1(rightPt, false);
93388 return new Segment(leftSE, rightSE, [ring], [winding]);
93395 var RingIn = /*#__PURE__*/function () {
93396 function RingIn(geomRing, poly, isExterior) {
93397 _classCallCheck$1(this, RingIn);
93399 if (!Array.isArray(geomRing) || geomRing.length === 0) {
93400 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93404 this.isExterior = isExterior;
93405 this.segments = [];
93407 if (typeof geomRing[0][0] !== 'number' || typeof geomRing[0][1] !== 'number') {
93408 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93411 var firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
93422 var prevPoint = firstPoint;
93424 for (var i = 1, iMax = geomRing.length; i < iMax; i++) {
93425 if (typeof geomRing[i][0] !== 'number' || typeof geomRing[i][1] !== 'number') {
93426 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93429 var point = rounder.round(geomRing[i][0], geomRing[i][1]); // skip repeated points
93431 if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
93432 this.segments.push(Segment.fromRing(prevPoint, point, this));
93433 if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
93434 if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
93435 if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
93436 if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
93438 } // add segment from last to first if last is not the same as first
93441 if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
93442 this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
93446 _createClass$1(RingIn, [{
93447 key: "getSweepEvents",
93448 value: function getSweepEvents() {
93449 var sweepEvents = [];
93451 for (var i = 0, iMax = this.segments.length; i < iMax; i++) {
93452 var segment = this.segments[i];
93453 sweepEvents.push(segment.leftSE);
93454 sweepEvents.push(segment.rightSE);
93457 return sweepEvents;
93464 var PolyIn = /*#__PURE__*/function () {
93465 function PolyIn(geomPoly, multiPoly) {
93466 _classCallCheck$1(this, PolyIn);
93468 if (!Array.isArray(geomPoly)) {
93469 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93472 this.exteriorRing = new RingIn(geomPoly[0], this, true); // copy by value
93476 x: this.exteriorRing.bbox.ll.x,
93477 y: this.exteriorRing.bbox.ll.y
93480 x: this.exteriorRing.bbox.ur.x,
93481 y: this.exteriorRing.bbox.ur.y
93484 this.interiorRings = [];
93486 for (var i = 1, iMax = geomPoly.length; i < iMax; i++) {
93487 var ring = new RingIn(geomPoly[i], this, false);
93488 if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
93489 if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
93490 if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
93491 if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
93492 this.interiorRings.push(ring);
93495 this.multiPoly = multiPoly;
93498 _createClass$1(PolyIn, [{
93499 key: "getSweepEvents",
93500 value: function getSweepEvents() {
93501 var sweepEvents = this.exteriorRing.getSweepEvents();
93503 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
93504 var ringSweepEvents = this.interiorRings[i].getSweepEvents();
93506 for (var j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
93507 sweepEvents.push(ringSweepEvents[j]);
93511 return sweepEvents;
93518 var MultiPolyIn = /*#__PURE__*/function () {
93519 function MultiPolyIn(geom, isSubject) {
93520 _classCallCheck$1(this, MultiPolyIn);
93522 if (!Array.isArray(geom)) {
93523 throw new Error('Input geometry is not a valid Polygon or MultiPolygon');
93527 // if the input looks like a polygon, convert it to a multipolygon
93528 if (typeof geom[0][0][0] === 'number') geom = [geom];
93529 } catch (ex) {// The input is either malformed or has empty arrays.
93530 // In either case, it will be handled later on.
93536 x: Number.POSITIVE_INFINITY,
93537 y: Number.POSITIVE_INFINITY
93540 x: Number.NEGATIVE_INFINITY,
93541 y: Number.NEGATIVE_INFINITY
93545 for (var i = 0, iMax = geom.length; i < iMax; i++) {
93546 var poly = new PolyIn(geom[i], this);
93547 if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
93548 if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
93549 if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
93550 if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
93551 this.polys.push(poly);
93554 this.isSubject = isSubject;
93557 _createClass$1(MultiPolyIn, [{
93558 key: "getSweepEvents",
93559 value: function getSweepEvents() {
93560 var sweepEvents = [];
93562 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
93563 var polySweepEvents = this.polys[i].getSweepEvents();
93565 for (var j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
93566 sweepEvents.push(polySweepEvents[j]);
93570 return sweepEvents;
93574 return MultiPolyIn;
93577 var RingOut = /*#__PURE__*/function () {
93578 _createClass$1(RingOut, null, [{
93581 /* Given the segments from the sweep line pass, compute & return a series
93582 * of closed rings from all the segments marked to be part of the result */
93583 value: function factory(allSegments) {
93586 for (var i = 0, iMax = allSegments.length; i < iMax; i++) {
93587 var segment = allSegments[i];
93588 if (!segment.isInResult() || segment.ringOut) continue;
93589 var prevEvent = null;
93590 var event = segment.leftSE;
93591 var nextEvent = segment.rightSE;
93592 var events = [event];
93593 var startingPoint = event.point;
93594 var intersectionLEs = [];
93595 /* Walk the chain of linked events to form a closed ring */
93600 events.push(event);
93601 /* Is the ring complete? */
93603 if (event.point === startingPoint) break;
93606 var availableLEs = event.getAvailableLinkedEvents();
93607 /* Did we hit a dead end? This shouldn't happen. Indicates some earlier
93608 * part of the algorithm malfunctioned... please file a bug report. */
93610 if (availableLEs.length === 0) {
93611 var firstPt = events[0].point;
93612 var lastPt = events[events.length - 1].point;
93613 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, "]."));
93615 /* Only one way to go, so cotinue on the path */
93618 if (availableLEs.length === 1) {
93619 nextEvent = availableLEs[0].otherSE;
93622 /* We must have an intersection. Check for a completed loop */
93625 var indexLE = null;
93627 for (var j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
93628 if (intersectionLEs[j].point === event.point) {
93633 /* Found a completed loop. Cut that off and make a ring */
93636 if (indexLE !== null) {
93637 var intersectionLE = intersectionLEs.splice(indexLE)[0];
93638 var ringEvents = events.splice(intersectionLE.index);
93639 ringEvents.unshift(ringEvents[0].otherSE);
93640 ringsOut.push(new RingOut(ringEvents.reverse()));
93643 /* register the intersection */
93646 intersectionLEs.push({
93647 index: events.length,
93650 /* Choose the left-most option to continue the walk */
93652 var comparator = event.getLeftmostComparator(prevEvent);
93653 nextEvent = availableLEs.sort(comparator)[0].otherSE;
93658 ringsOut.push(new RingOut(events));
93665 function RingOut(events) {
93666 _classCallCheck$1(this, RingOut);
93668 this.events = events;
93670 for (var i = 0, iMax = events.length; i < iMax; i++) {
93671 events[i].segment.ringOut = this;
93677 _createClass$1(RingOut, [{
93679 value: function getGeom() {
93680 // Remove superfluous points (ie extra points along a straight line),
93681 var prevPt = this.events[0].point;
93682 var points = [prevPt];
93684 for (var i = 1, iMax = this.events.length - 1; i < iMax; i++) {
93685 var _pt = this.events[i].point;
93686 var _nextPt = this.events[i + 1].point;
93687 if (compareVectorAngles(_pt, prevPt, _nextPt) === 0) continue;
93690 } // ring was all (within rounding error of angle calc) colinear points
93693 if (points.length === 1) return null; // check if the starting point is necessary
93695 var pt = points[0];
93696 var nextPt = points[1];
93697 if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
93698 points.push(points[0]);
93699 var step = this.isExteriorRing() ? 1 : -1;
93700 var iStart = this.isExteriorRing() ? 0 : points.length - 1;
93701 var iEnd = this.isExteriorRing() ? points.length : -1;
93702 var orderedPoints = [];
93704 for (var _i = iStart; _i != iEnd; _i += step) {
93705 orderedPoints.push([points[_i].x, points[_i].y]);
93708 return orderedPoints;
93711 key: "isExteriorRing",
93712 value: function isExteriorRing() {
93713 if (this._isExteriorRing === undefined) {
93714 var enclosing = this.enclosingRing();
93715 this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
93718 return this._isExteriorRing;
93721 key: "enclosingRing",
93722 value: function enclosingRing() {
93723 if (this._enclosingRing === undefined) {
93724 this._enclosingRing = this._calcEnclosingRing();
93727 return this._enclosingRing;
93729 /* Returns the ring that encloses this one, if any */
93732 key: "_calcEnclosingRing",
93733 value: function _calcEnclosingRing() {
93734 // start with the ealier sweep line event so that the prevSeg
93735 // chain doesn't lead us inside of a loop of ours
93736 var leftMostEvt = this.events[0];
93738 for (var i = 1, iMax = this.events.length; i < iMax; i++) {
93739 var evt = this.events[i];
93740 if (SweepEvent$1.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
93743 var prevSeg = leftMostEvt.segment.prevInResult();
93744 var prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
93747 // no segment found, thus no ring can enclose us
93748 if (!prevSeg) return null; // no segments below prev segment found, thus the ring of the prev
93749 // segment must loop back around and enclose us
93751 if (!prevPrevSeg) return prevSeg.ringOut; // if the two segments are of different rings, the ring of the prev
93752 // segment must either loop around us or the ring of the prev prev
93753 // seg, which would make us and the ring of the prev peers
93755 if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
93756 if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) {
93757 return prevSeg.ringOut;
93758 } else return prevSeg.ringOut.enclosingRing();
93759 } // two segments are from the same ring, so this was a penisula
93760 // of that ring. iterate downward, keep searching
93763 prevSeg = prevPrevSeg.prevInResult();
93764 prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
93772 var PolyOut = /*#__PURE__*/function () {
93773 function PolyOut(exteriorRing) {
93774 _classCallCheck$1(this, PolyOut);
93776 this.exteriorRing = exteriorRing;
93777 exteriorRing.poly = this;
93778 this.interiorRings = [];
93781 _createClass$1(PolyOut, [{
93782 key: "addInterior",
93783 value: function addInterior(ring) {
93784 this.interiorRings.push(ring);
93789 value: function getGeom() {
93790 var geom = [this.exteriorRing.getGeom()]; // exterior ring was all (within rounding error of angle calc) colinear points
93792 if (geom[0] === null) return null;
93794 for (var i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
93795 var ringGeom = this.interiorRings[i].getGeom(); // interior ring was all (within rounding error of angle calc) colinear points
93797 if (ringGeom === null) continue;
93798 geom.push(ringGeom);
93808 var MultiPolyOut = /*#__PURE__*/function () {
93809 function MultiPolyOut(rings) {
93810 _classCallCheck$1(this, MultiPolyOut);
93812 this.rings = rings;
93813 this.polys = this._composePolys(rings);
93816 _createClass$1(MultiPolyOut, [{
93818 value: function getGeom() {
93821 for (var i = 0, iMax = this.polys.length; i < iMax; i++) {
93822 var polyGeom = this.polys[i].getGeom(); // exterior ring was all (within rounding error of angle calc) colinear points
93824 if (polyGeom === null) continue;
93825 geom.push(polyGeom);
93831 key: "_composePolys",
93832 value: function _composePolys(rings) {
93835 for (var i = 0, iMax = rings.length; i < iMax; i++) {
93836 var ring = rings[i];
93837 if (ring.poly) continue;
93838 if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else {
93839 var enclosingRing = ring.enclosingRing();
93840 if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
93841 enclosingRing.poly.addInterior(ring);
93849 return MultiPolyOut;
93852 * NOTE: We must be careful not to change any segments while
93853 * they are in the SplayTree. AFAIK, there's no way to tell
93854 * the tree to rebalance itself - thus before splitting
93855 * a segment that's in the tree, we remove it from the tree,
93856 * do the split, then re-insert it. (Even though splitting a
93857 * segment *shouldn't* change its correct position in the
93858 * sweep line tree, the reality is because of rounding errors,
93859 * it sometimes does.)
93863 var SweepLine = /*#__PURE__*/function () {
93864 function SweepLine(queue) {
93865 var comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare;
93867 _classCallCheck$1(this, SweepLine);
93869 this.queue = queue;
93870 this.tree = new Tree(comparator);
93871 this.segments = [];
93874 _createClass$1(SweepLine, [{
93876 value: function process(event) {
93877 var segment = event.segment;
93878 var newEvents = []; // if we've already been consumed by another segment,
93879 // clean up our body parts and get out
93881 if (event.consumedBy) {
93882 if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment);
93886 var node = event.isLeft ? this.tree.insert(segment) : this.tree.find(segment);
93887 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.');
93888 var prevNode = node;
93889 var nextNode = node;
93890 var prevSeg = undefined;
93891 var nextSeg = undefined; // skip consumed segments still in tree
93893 while (prevSeg === undefined) {
93894 prevNode = this.tree.prev(prevNode);
93895 if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key;
93896 } // skip consumed segments still in tree
93899 while (nextSeg === undefined) {
93900 nextNode = this.tree.next(nextNode);
93901 if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key;
93904 if (event.isLeft) {
93905 // Check for intersections against the previous segment in the sweep line
93906 var prevMySplitter = null;
93909 var prevInter = prevSeg.getIntersection(segment);
93911 if (prevInter !== null) {
93912 if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
93914 if (!prevSeg.isAnEndpoint(prevInter)) {
93915 var newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
93917 for (var i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
93918 newEvents.push(newEventsFromSplit[i]);
93922 } // Check for intersections against the next segment in the sweep line
93925 var nextMySplitter = null;
93928 var nextInter = nextSeg.getIntersection(segment);
93930 if (nextInter !== null) {
93931 if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
93933 if (!nextSeg.isAnEndpoint(nextInter)) {
93934 var _newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
93936 for (var _i = 0, _iMax = _newEventsFromSplit.length; _i < _iMax; _i++) {
93937 newEvents.push(_newEventsFromSplit[_i]);
93941 } // For simplicity, even if we find more than one intersection we only
93942 // spilt on the 'earliest' (sweep-line style) of the intersections.
93943 // The other intersection will be handled in a future process().
93946 if (prevMySplitter !== null || nextMySplitter !== null) {
93947 var mySplitter = null;
93948 if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else {
93949 var cmpSplitters = SweepEvent$1.comparePoints(prevMySplitter, nextMySplitter);
93950 mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
93951 } // Rounding errors can cause changes in ordering,
93952 // so remove afected segments and right sweep events before splitting
93954 this.queue.remove(segment.rightSE);
93955 newEvents.push(segment.rightSE);
93957 var _newEventsFromSplit2 = segment.split(mySplitter);
93959 for (var _i2 = 0, _iMax2 = _newEventsFromSplit2.length; _i2 < _iMax2; _i2++) {
93960 newEvents.push(_newEventsFromSplit2[_i2]);
93964 if (newEvents.length > 0) {
93965 // We found some intersections, so re-do the current event to
93966 // make sure sweep line ordering is totally consistent for later
93967 // use with the segment 'prev' pointers
93968 this.tree.remove(segment);
93969 newEvents.push(event);
93971 // done with left event
93972 this.segments.push(segment);
93973 segment.prev = prevSeg;
93977 // since we're about to be removed from the sweep line, check for
93978 // intersections between our previous and next segments
93979 if (prevSeg && nextSeg) {
93980 var inter = prevSeg.getIntersection(nextSeg);
93982 if (inter !== null) {
93983 if (!prevSeg.isAnEndpoint(inter)) {
93984 var _newEventsFromSplit3 = this._splitSafely(prevSeg, inter);
93986 for (var _i3 = 0, _iMax3 = _newEventsFromSplit3.length; _i3 < _iMax3; _i3++) {
93987 newEvents.push(_newEventsFromSplit3[_i3]);
93991 if (!nextSeg.isAnEndpoint(inter)) {
93992 var _newEventsFromSplit4 = this._splitSafely(nextSeg, inter);
93994 for (var _i4 = 0, _iMax4 = _newEventsFromSplit4.length; _i4 < _iMax4; _i4++) {
93995 newEvents.push(_newEventsFromSplit4[_i4]);
94001 this.tree.remove(segment);
94006 /* Safely split a segment that is currently in the datastructures
94007 * IE - a segment other than the one that is currently being processed. */
94010 key: "_splitSafely",
94011 value: function _splitSafely(seg, pt) {
94012 // Rounding errors can cause changes in ordering,
94013 // so remove afected segments and right sweep events before splitting
94014 // removeNode() doesn't work, so have re-find the seg
94015 // https://github.com/w8r/splay-tree/pull/5
94016 this.tree.remove(seg);
94017 var rightSE = seg.rightSE;
94018 this.queue.remove(rightSE);
94019 var newEvents = seg.split(pt);
94020 newEvents.push(rightSE); // splitting can trigger consumption
94022 if (seg.consumedBy === undefined) this.tree.insert(seg);
94030 var POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000;
94031 var POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== 'undefined' && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000;
94033 var Operation = /*#__PURE__*/function () {
94034 function Operation() {
94035 _classCallCheck$1(this, Operation);
94038 _createClass$1(Operation, [{
94040 value: function run(type, geom, moreGeoms) {
94041 operation.type = type;
94043 /* Convert inputs to MultiPoly objects */
94045 var multipolys = [new MultiPolyIn(geom, true)];
94047 for (var i = 0, iMax = moreGeoms.length; i < iMax; i++) {
94048 multipolys.push(new MultiPolyIn(moreGeoms[i], false));
94051 operation.numMultiPolys = multipolys.length;
94052 /* BBox optimization for difference operation
94053 * If the bbox of a multipolygon that's part of the clipping doesn't
94054 * intersect the bbox of the subject at all, we can just drop that
94057 if (operation.type === 'difference') {
94058 // in place removal
94059 var subject = multipolys[0];
94062 while (_i < multipolys.length) {
94063 if (getBboxOverlap(multipolys[_i].bbox, subject.bbox) !== null) _i++;else multipolys.splice(_i, 1);
94066 /* BBox optimization for intersection operation
94067 * If we can find any pair of multipolygons whose bbox does not overlap,
94068 * then the result will be empty. */
94071 if (operation.type === 'intersection') {
94072 // TODO: this is O(n^2) in number of polygons. By sorting the bboxes,
94073 // it could be optimized to O(n * ln(n))
94074 for (var _i2 = 0, _iMax = multipolys.length; _i2 < _iMax; _i2++) {
94075 var mpA = multipolys[_i2];
94077 for (var j = _i2 + 1, jMax = multipolys.length; j < jMax; j++) {
94078 if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
94082 /* Put segment endpoints in a priority queue */
94085 var queue = new Tree(SweepEvent$1.compare);
94087 for (var _i3 = 0, _iMax2 = multipolys.length; _i3 < _iMax2; _i3++) {
94088 var sweepEvents = multipolys[_i3].getSweepEvents();
94090 for (var _j = 0, _jMax = sweepEvents.length; _j < _jMax; _j++) {
94091 queue.insert(sweepEvents[_j]);
94093 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
94094 // prevents an infinite loop, an otherwise common manifestation of bugs
94095 throw new Error('Infinite loop when putting segment endpoints in a priority queue ' + '(queue size too big). Please file a bug report.');
94099 /* Pass the sweep line over those endpoints */
94102 var sweepLine = new SweepLine(queue);
94103 var prevQueueSize = queue.size;
94104 var node = queue.pop();
94107 var evt = node.key;
94109 if (queue.size === prevQueueSize) {
94110 // prevents an infinite loop, an otherwise common manifestation of bugs
94111 var seg = evt.segment;
94112 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.');
94115 if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
94116 // prevents an infinite loop, an otherwise common manifestation of bugs
94117 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(queue size too big). Please file a bug report.');
94120 if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) {
94121 // prevents an infinite loop, an otherwise common manifestation of bugs
94122 throw new Error('Infinite loop when passing sweep line over endpoints ' + '(too many sweep line segments). Please file a bug report.');
94125 var newEvents = sweepLine.process(evt);
94127 for (var _i4 = 0, _iMax3 = newEvents.length; _i4 < _iMax3; _i4++) {
94128 var _evt = newEvents[_i4];
94129 if (_evt.consumedBy === undefined) queue.insert(_evt);
94132 prevQueueSize = queue.size;
94133 node = queue.pop();
94134 } // free some memory we don't need anymore
94138 /* Collect and compile segments we're keeping into a multipolygon */
94140 var ringsOut = RingOut.factory(sweepLine.segments);
94141 var result = new MultiPolyOut(ringsOut);
94142 return result.getGeom();
94147 }(); // singleton available by import
94150 var operation = new Operation();
94152 var union$1 = function union(geom) {
94153 for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
94154 moreGeoms[_key - 1] = arguments[_key];
94157 return operation.run('union', geom, moreGeoms);
94160 var intersection$1$1 = function intersection(geom) {
94161 for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
94162 moreGeoms[_key2 - 1] = arguments[_key2];
94165 return operation.run('intersection', geom, moreGeoms);
94168 var xor = function xor(geom) {
94169 for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
94170 moreGeoms[_key3 - 1] = arguments[_key3];
94173 return operation.run('xor', geom, moreGeoms);
94176 var difference = function difference(subjectGeom) {
94177 for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
94178 clippingGeoms[_key4 - 1] = arguments[_key4];
94181 return operation.run('difference', subjectGeom, clippingGeoms);
94186 intersection: intersection$1$1,
94188 difference: difference
94191 var geojsonPrecision = createCommonjsModule(function (module) {
94193 function parse(t, coordinatePrecision, extrasPrecision) {
94194 function point(p) {
94195 return p.map(function (e, index) {
94197 return 1 * e.toFixed(coordinatePrecision);
94199 return 1 * e.toFixed(extrasPrecision);
94204 function multi(l) {
94205 return l.map(point);
94209 return p.map(multi);
94212 function multiPoly(m) {
94213 return m.map(poly);
94216 function geometry(obj) {
94221 switch (obj.type) {
94223 obj.coordinates = point(obj.coordinates);
94228 obj.coordinates = multi(obj.coordinates);
94232 case "MultiLineString":
94233 obj.coordinates = poly(obj.coordinates);
94236 case "MultiPolygon":
94237 obj.coordinates = multiPoly(obj.coordinates);
94240 case "GeometryCollection":
94241 obj.geometries = obj.geometries.map(geometry);
94249 function feature(obj) {
94250 obj.geometry = geometry(obj.geometry);
94254 function featureCollection(f) {
94255 f.features = f.features.map(feature);
94259 function geometryCollection(g) {
94260 g.geometries = g.geometries.map(geometry);
94272 case "GeometryCollection":
94273 return geometryCollection(t);
94275 case "FeatureCollection":
94276 return featureCollection(t);
94282 case "MultiPolygon":
94283 case "MultiLineString":
94284 return geometry(t);
94291 module.exports = parse;
94292 module.exports.parse = parse;
94296 function isObject$4(obj) {
94297 return _typeof(obj) === 'object' && obj !== null;
94300 function forEach(obj, cb) {
94301 if (Array.isArray(obj)) {
94303 } else if (isObject$4(obj)) {
94304 Object.keys(obj).forEach(function (key) {
94305 var val = obj[key];
94311 function getTreeDepth(obj) {
94314 if (Array.isArray(obj) || isObject$4(obj)) {
94315 forEach(obj, function (val) {
94316 if (Array.isArray(val) || isObject$4(val)) {
94317 var tmpDepth = getTreeDepth(val);
94319 if (tmpDepth > depth) {
94330 function stringify(obj, options) {
94331 options = options || {};
94332 var indent = JSON.stringify([1], null, get$5(options, 'indent', 2)).slice(2, -3);
94333 var addMargin = get$5(options, 'margins', false);
94334 var addArrayMargin = get$5(options, 'arrayMargins', false);
94335 var addObjectMargin = get$5(options, 'objectMargins', false);
94336 var maxLength = indent === '' ? Infinity : get$5(options, 'maxLength', 80);
94337 var maxNesting = get$5(options, 'maxNesting', Infinity);
94338 return function _stringify(obj, currentIndent, reserved) {
94339 if (obj && typeof obj.toJSON === 'function') {
94340 obj = obj.toJSON();
94343 var string = JSON.stringify(obj);
94345 if (string === undefined) {
94349 var length = maxLength - currentIndent.length - reserved;
94350 var treeDepth = getTreeDepth(obj);
94352 if (treeDepth <= maxNesting && string.length <= length) {
94353 var prettified = prettify(string, {
94354 addMargin: addMargin,
94355 addArrayMargin: addArrayMargin,
94356 addObjectMargin: addObjectMargin
94359 if (prettified.length <= length) {
94364 if (isObject$4(obj)) {
94365 var nextIndent = currentIndent + indent;
94369 var comma = function comma(array, index) {
94370 return index === array.length - 1 ? 0 : 1;
94373 if (Array.isArray(obj)) {
94374 for (var index = 0; index < obj.length; index++) {
94375 items.push(_stringify(obj[index], nextIndent, comma(obj, index)) || 'null');
94380 Object.keys(obj).forEach(function (key, index, array) {
94381 var keyPart = JSON.stringify(key) + ': ';
94383 var value = _stringify(obj[key], nextIndent, keyPart.length + comma(array, index));
94385 if (value !== undefined) {
94386 items.push(keyPart + value);
94392 if (items.length > 0) {
94393 return [delimiters[0], indent + items.join(',\n' + nextIndent), delimiters[1]].join('\n' + currentIndent);
94399 } // Note: This regex matches even invalid JSON strings, but since we’re
94400 // working on the output of `JSON.stringify` we know that only valid strings
94401 // are present (unless the user supplied a weird `options.indent` but in
94402 // that case we don’t care since the output would be invalid anyway).
94405 var stringOrChar = /("(?:[^\\"]|\\.)*")|[:,\][}{]/g;
94407 function prettify(string, options) {
94408 options = options || {};
94418 if (options.addMargin || options.addObjectMargin) {
94419 tokens['{'] = '{ ';
94420 tokens['}'] = ' }';
94423 if (options.addMargin || options.addArrayMargin) {
94424 tokens['['] = '[ ';
94425 tokens[']'] = ' ]';
94428 return string.replace(stringOrChar, function (match, string) {
94429 return string ? match : tokens[match];
94433 function get$5(options, name, defaultValue) {
94434 return name in options ? options[name] : defaultValue;
94437 var jsonStringifyPrettyCompact = stringify;
94439 var _default$3 = /*#__PURE__*/function () {
94442 // `fc` Optional FeatureCollection of known features
94444 // Optionally pass a GeoJSON FeatureCollection of known features which we can refer to later.
94445 // Each feature must have a filename-like `id`, for example: `something.geojson`
94448 // "type": "FeatureCollection"
94451 // "type": "Feature",
94452 // "id": "philly_metro.geojson",
94453 // "properties": { … },
94454 // "geometry": { … }
94458 function _default(fc) {
94461 _classCallCheck(this, _default);
94463 // The _cache retains resolved features, so if you ask for the same thing multiple times
94464 // we don't repeat the expensive resolving/clipping operations.
94466 // Each feature has a stable identifier that is used as the cache key.
94467 // The identifiers look like:
94468 // - for point locations, the stringified point: e.g. '[8.67039,49.41882]'
94469 // - for geojson locations, the geojson id: e.g. 'de-hamburg.geojson'
94470 // - for countrycoder locations, feature.id property: e.g. 'Q2' (countrycoder uses Wikidata identifiers)
94471 // - for aggregated locationSets, +[include]-[exclude]: e.g '+[Q2]-[Q18,Q27611]'
94472 this._cache = {}; // When strict mode = true, throw on invalid locations or locationSets.
94473 // When strict mode = false, return `null` for invalid locations or locationSets.
94475 this._strict = true; // process input FeatureCollection
94477 if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
94478 fc.features.forEach(function (feature) {
94479 feature.properties = feature.properties || {};
94480 var props = feature.properties; // get `id` from either `id` or `properties`
94482 var id = feature.id || props.id;
94483 if (!id || !/^\S+\.geojson$/i.test(id)) return; // ensure `id` exists and is lowercase
94485 id = id.toLowerCase();
94487 props.id = id; // ensure `area` property exists
94490 var area = geojsonArea.geometry(feature.geometry) / 1e6; // m² to km²
94492 props.area = Number(area.toFixed(2));
94495 _this._cache[id] = feature;
94497 } // Replace CountryCoder world geometry to be a polygon covering the world.
94500 var world = _cloneDeep(feature('Q2'));
94504 coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
94507 world.properties.id = 'Q2';
94508 world.properties.area = geojsonArea.geometry(world.geometry) / 1e6; // m² to km²
94510 this._cache.Q2 = world;
94511 } // validateLocation
94512 // `location` The location to validate
94514 // Pass a `location` value to validate
94516 // Returns a result like:
94518 // type: 'point', 'geojson', or 'countrycoder'
94519 // location: the queried location
94520 // id: the stable identifier for the feature
94522 // or `null` if the location is invalid
94526 _createClass(_default, [{
94527 key: "validateLocation",
94528 value: function validateLocation(location) {
94529 if (Array.isArray(location)) {
94530 // a [lon,lat] coordinate pair?
94531 if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) && location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90) {
94532 var id = '[' + location.toString() + ']';
94535 location: location,
94539 } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) {
94540 // a .geojson filename?
94541 var _id = location.toLowerCase();
94543 if (this._cache[_id]) {
94546 location: location,
94550 } else if (typeof location === 'string' || typeof location === 'number') {
94551 // a country-coder value?
94552 var feature$1 = feature(location);
94555 // Use wikidata QID as the identifier, since that seems to be the one
94556 // property that everything in CountryCoder is guaranteed to have.
94557 var _id2 = feature$1.properties.wikidata;
94559 type: 'countrycoder',
94560 location: location,
94566 if (this._strict) {
94567 throw new Error("validateLocation: Invalid location: \"".concat(location, "\"."));
94571 } // resolveLocation
94572 // `location` The location to resolve
94574 // Pass a `location` value to resolve
94576 // Returns a result like:
94578 // type: 'point', 'geojson', or 'countrycoder'
94579 // location: the queried location
94580 // id: a stable identifier for the feature
94581 // feature: the resolved GeoJSON feature
94583 // or `null` if the location is invalid
94587 key: "resolveLocation",
94588 value: function resolveLocation(location) {
94589 var valid = this.validateLocation(location);
94590 if (!valid) return null;
94591 var id = valid.id; // return a result from cache if we can
94593 if (this._cache[id]) {
94594 return Object.assign(valid, {
94595 feature: this._cache[id]
94597 } // a [lon,lat] coordinate pair?
94600 if (valid.type === 'point') {
94601 var RADIUS = 25000; // meters
94605 var area = Math.PI * RADIUS * RADIUS / 1e6; // m² to km²
94607 var feature$1 = this._cache[id] = geojsonPrecision({
94612 area: Number(area.toFixed(2))
94614 geometry: circleToPolygon(location, RADIUS, EDGES)
94616 return Object.assign(valid, {
94618 }); // a .geojson filename?
94619 } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
94620 var _feature = _cloneDeep(feature(id));
94622 var props = _feature.properties; // -> This block of code is weird and requires some explanation. <-
94623 // CountryCoder includes higher level features which are made up of members.
94624 // These features don't have their own geometry, but CountryCoder provides an
94625 // `aggregateFeature` method to combine these members into a MultiPolygon.
94626 // BUT, when we try to actually work with these aggregated MultiPolygons,
94627 // Turf/JSTS gets crashy because of topography bugs.
94628 // SO, we'll aggregate the features ourselves by unioning them together.
94629 // This approach also has the benefit of removing all the internal boaders and
94630 // simplifying the regional polygons a lot.
94632 if (Array.isArray(props.members)) {
94633 var seed = _feature.geometry ? _feature : null;
94634 var aggregate = props.members.reduce(_locationReducer.bind(this), seed);
94635 _feature.geometry = aggregate.geometry;
94636 } // ensure `area` property exists
94640 var _area = geojsonArea.geometry(_feature.geometry) / 1e6; // m² to km²
94643 props.area = Number(_area.toFixed(2));
94644 } // ensure `id` property exists
94649 this._cache[id] = _feature;
94650 return Object.assign(valid, {
94655 if (this._strict) {
94656 throw new Error("resolveLocation: Couldn't resolve location \"".concat(location, "\"."));
94660 } // validateLocationSet
94661 // `locationSet` the locationSet to validate
94663 // Pass a locationSet Object to validate like:
94665 // include: [ Array of locations ],
94666 // exclude: [ Array of locations ]
94669 // Returns a result like:
94671 // type: 'locationset'
94672 // locationSet: the queried locationSet
94673 // id: the stable identifier for the feature
94675 // or `null` if the locationSet is invalid
94679 key: "validateLocationSet",
94680 value: function validateLocationSet(locationSet) {
94681 locationSet = locationSet || {};
94682 var validator = this.validateLocation.bind(this);
94683 var include = (locationSet.include || []).map(validator).filter(Boolean);
94684 var exclude = (locationSet.exclude || []).map(validator).filter(Boolean);
94686 if (!include.length) {
94687 if (this._strict) {
94688 throw new Error("validateLocationSet: LocationSet includes nothing.");
94690 // non-strict mode, replace an empty locationSet with one that includes "the world"
94691 locationSet.include = ['Q2'];
94693 type: 'countrycoder',
94698 } // generate stable identifier
94701 include.sort(_sortLocations);
94702 var id = '+[' + include.map(function (d) {
94704 }).join(',') + ']';
94706 if (exclude.length) {
94707 exclude.sort(_sortLocations);
94708 id += '-[' + exclude.map(function (d) {
94710 }).join(',') + ']';
94714 type: 'locationset',
94715 locationSet: locationSet,
94718 } // resolveLocationSet
94719 // `locationSet` the locationSet to resolve
94721 // Pass a locationSet Object to validate like:
94723 // include: [ Array of locations ],
94724 // exclude: [ Array of locations ]
94727 // Returns a result like:
94729 // type: 'locationset'
94730 // locationSet: the queried locationSet
94731 // id: the stable identifier for the feature
94732 // feature: the resolved GeoJSON feature
94734 // or `null` if the locationSet is invalid
94738 key: "resolveLocationSet",
94739 value: function resolveLocationSet(locationSet) {
94740 locationSet = locationSet || {};
94741 var valid = this.validateLocationSet(locationSet);
94742 if (!valid) return null;
94743 var id = valid.id; // return a result from cache if we can
94745 if (this._cache[id]) {
94746 return Object.assign(valid, {
94747 feature: this._cache[id]
94751 var resolver = this.resolveLocation.bind(this);
94752 var include = (locationSet.include || []).map(resolver).filter(Boolean);
94753 var exclude = (locationSet.exclude || []).map(resolver).filter(Boolean); // return quickly if it's a single included location..
94755 if (include.length === 1 && exclude.length === 0) {
94756 return Object.assign(valid, {
94757 feature: include[0].feature
94759 } // calculate unions
94762 var includeGeoJSON = include.map(function (d) {
94764 }).reduce(_locationReducer.bind(this), null);
94765 var excludeGeoJSON = exclude.map(function (d) {
94767 }).reduce(_locationReducer.bind(this), null); // calculate difference, update `area` and return result
94769 var resultGeoJSON = excludeGeoJSON ? _clip(includeGeoJSON, excludeGeoJSON, 'DIFFERENCE') : includeGeoJSON;
94770 var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6; // m² to km²
94772 resultGeoJSON.id = id;
94773 resultGeoJSON.properties = {
94775 area: Number(area.toFixed(2))
94777 this._cache[id] = resultGeoJSON;
94778 return Object.assign(valid, {
94779 feature: resultGeoJSON
94786 value: function strict(val) {
94787 if (val === undefined) {
94789 return this._strict;
94792 this._strict = val;
94796 // convenience method to access the internal cache
94800 value: function cache() {
94801 return this._cache;
94803 // convenience method to prettyStringify the given object
94807 value: function stringify(obj, options) {
94808 return jsonStringifyPrettyCompact(obj, options);
94813 }(); // Wrap the mfogel/polygon-clipping library and return a GeoJSON feature.
94815 function _clip(a, b, which) {
94817 UNION: index$1.union,
94818 DIFFERENCE: index$1.difference
94820 var coords = fn(a.geometry.coordinates, b.geometry.coordinates);
94825 type: whichType(coords),
94826 coordinates: coords
94828 }; // is this a Polygon or a MultiPolygon?
94830 function whichType(coords) {
94831 var a = Array.isArray(coords);
94832 var b = a && Array.isArray(coords[0]);
94833 var c = b && Array.isArray(coords[0][0]);
94834 var d = c && Array.isArray(coords[0][0][0]);
94835 return d ? 'MultiPolygon' : 'Polygon';
94837 } // Reduce an array of locations into a single GeoJSON feature
94840 function _locationReducer(accumulator, location) {
94841 /* eslint-disable no-console, no-invalid-this */
94845 var resolved = this.resolveLocation(location);
94847 if (!resolved || !resolved.feature) {
94848 console.warn("Warning: Couldn't resolve location \"".concat(location, "\""));
94849 return accumulator;
94852 result = !accumulator ? resolved.feature : _clip(accumulator, resolved.feature, 'UNION');
94854 console.warn("Warning: Error resolving location \"".concat(location, "\""));
94856 result = accumulator;
94860 /* eslint-enable no-console, no-invalid-this */
94863 function _cloneDeep(obj) {
94864 return JSON.parse(JSON.stringify(obj));
94865 } // Sorting the location lists is ok because they end up unioned together.
94866 // This sorting makes it possible to generate a deterministic id.
94869 function _sortLocations(a, b) {
94875 var aRank = rank[a.type];
94876 var bRank = rank[b.type];
94877 return aRank > bRank ? 1 : aRank < bRank ? -1 : a.id.localeCompare(b.id);
94881 function uiSuccess(context) {
94883 var dispatch$1 = dispatch('cancel');
94889 ensureOSMCommunityIndex(); // start fetching the data
94891 function ensureOSMCommunityIndex() {
94892 var data = _mainFileFetcher;
94893 return Promise.all([data.get('oci_resources'), data.get('oci_features')]).then(function (vals) {
94894 if (_oci) return _oci;
94895 var ociResources = vals[0].resources;
94896 var loco = new _default$3(vals[1]);
94897 var ociFeatures = {};
94898 Object.values(ociResources).forEach(function (resource) {
94899 var feature = loco.resolveLocationSet(resource.locationSet).feature;
94900 var ociFeature = ociFeatures[feature.id];
94903 ociFeature = JSON.parse(JSON.stringify(feature)); // deep clone
94905 ociFeature.properties.resourceIDs = new Set();
94906 ociFeatures[feature.id] = ociFeature;
94909 ociFeature.properties.resourceIDs.add(resource.id);
94912 features: ociFeatures,
94913 resources: ociResources,
94914 query: whichPolygon_1({
94915 type: 'FeatureCollection',
94916 features: Object.values(ociFeatures)
94920 } // string-to-date parsing in JavaScript is weird
94923 function parseEventDate(when) {
94925 var raw = when.trim();
94928 if (!/Z$/.test(raw)) {
94929 // if no trailing 'Z', add one
94930 raw += 'Z'; // this forces date to be parsed as a UTC date
94933 var parsed = new Date(raw);
94934 return new Date(parsed.toUTCString().substr(0, 25)); // convert to local timezone
94937 function success(selection) {
94938 var header = selection.append('div').attr('class', 'header fillL');
94939 header.append('h3').html(_t.html('success.just_edited'));
94940 header.append('button').attr('class', 'close').on('click', function () {
94941 return dispatch$1.call('cancel');
94942 }).call(svgIcon('#iD-icon-close'));
94943 var body = selection.append('div').attr('class', 'body save-success fillL');
94944 var summary = body.append('div').attr('class', 'save-summary');
94945 summary.append('h3').html(_t.html('success.thank_you' + (_location ? '_location' : ''), {
94948 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'));
94949 var osm = context.connection();
94951 var changesetURL = osm.changesetURL(_changeset.id);
94952 var table = summary.append('table').attr('class', 'summary-table');
94953 var row = table.append('tr').attr('class', 'summary-row');
94954 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');
94955 var summaryDetail = row.append('td').attr('class', 'cell-detail summary-detail');
94956 summaryDetail.append('a').attr('class', 'cell-detail summary-view-on-osm').attr('target', '_blank').attr('href', changesetURL).html(_t.html('success.view_on_osm'));
94957 summaryDetail.append('div').html(_t.html('success.changeset_id', {
94958 changeset_id: "<a href=\"".concat(changesetURL, "\" target=\"_blank\">").concat(_changeset.id, "</a>")
94959 })); // Get OSM community index features intersecting the map..
94961 ensureOSMCommunityIndex().then(function (oci) {
94962 var communities = [];
94963 var properties = oci.query(context.map().center(), true) || []; // Gather the communities from the result
94965 properties.forEach(function (props) {
94966 var resourceIDs = Array.from(props.resourceIDs);
94967 resourceIDs.forEach(function (resourceID) {
94968 var resource = oci.resources[resourceID];
94970 area: props.area || Infinity,
94971 order: resource.order || 0,
94975 }); // sort communities by feature area ascending, community order descending
94977 communities.sort(function (a, b) {
94978 return a.area - b.area || b.order - a.order;
94980 body.call(showCommunityLinks, communities.map(function (c) {
94986 function showCommunityLinks(selection, resources) {
94987 var communityLinks = selection.append('div').attr('class', 'save-communityLinks');
94988 communityLinks.append('h3').html(_t.html('success.like_osm'));
94989 var table = communityLinks.append('table').attr('class', 'community-table');
94990 var row = table.selectAll('.community-row').data(resources);
94991 var rowEnter = row.enter().append('tr').attr('class', 'community-row');
94992 rowEnter.append('td').attr('class', 'cell-icon community-icon').append('a').attr('target', '_blank').attr('href', function (d) {
94994 }).append('svg').attr('class', 'logo-small').append('use').attr('xlink:href', function (d) {
94995 return "#community-".concat(d.type);
94997 var communityDetail = rowEnter.append('td').attr('class', 'cell-detail community-detail');
94998 communityDetail.each(showCommunityDetails);
94999 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'));
95002 function showCommunityDetails(d) {
95003 var selection = select(this);
95004 var communityID = d.id;
95005 var replacements = {
95006 url: linkify(d.url),
95007 signupUrl: linkify(d.signupUrl || d.url)
95009 selection.append('div').attr('class', 'community-name').append('a').attr('target', '_blank').attr('href', d.url).html(_t.html("community.".concat(d.id, ".name")));
95010 var descriptionHTML = _t.html("community.".concat(d.id, ".description"), replacements);
95012 if (d.type === 'reddit') {
95013 // linkify subreddits #4997
95014 descriptionHTML = descriptionHTML.replace(/(\/r\/\w*\/*)/i, function (match) {
95015 return linkify(d.url, match);
95019 selection.append('div').attr('class', 'community-description').html(descriptionHTML);
95021 if (d.extendedDescription || d.languageCodes && d.languageCodes.length) {
95022 selection.append('div').call(uiDisclosure(context, "community-more-".concat(d.id), false).expanded(false).updatePreference(false).label(_t.html('success.more')).content(showMore));
95025 var nextEvents = (d.events || []).map(function (event) {
95026 event.date = parseEventDate(event.when);
95028 }).filter(function (event) {
95029 // date is valid and future (or today)
95030 var t = event.date.getTime();
95031 var now = new Date().setHours(0, 0, 0, 0);
95032 return !isNaN(t) && t >= now;
95033 }).sort(function (a, b) {
95034 // sort by date ascending
95035 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
95036 }).slice(0, MAXEVENTS); // limit number of events shown
95038 if (nextEvents.length) {
95039 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);
95042 function showMore(selection) {
95043 var more = selection.selectAll('.community-more').data([0]);
95044 var moreEnter = more.enter().append('div').attr('class', 'community-more');
95046 if (d.extendedDescription) {
95047 moreEnter.append('div').attr('class', 'community-extended-description').html(_t.html("community.".concat(d.id, ".extendedDescription"), replacements));
95050 if (d.languageCodes && d.languageCodes.length) {
95051 var languageList = d.languageCodes.map(function (code) {
95052 return _mainLocalizer.languageName(code);
95054 moreEnter.append('div').attr('class', 'community-languages').html(_t.html('success.languages', {
95055 languages: languageList
95060 function showNextEvents(selection) {
95061 var events = selection.append('div').attr('class', 'community-events');
95062 var item = events.selectAll('.community-event').data(nextEvents);
95063 var itemEnter = item.enter().append('div').attr('class', 'community-event');
95064 itemEnter.append('div').attr('class', 'community-event-name').append('a').attr('target', '_blank').attr('href', function (d) {
95066 }).html(function (d) {
95069 if (d.i18n && d.id) {
95070 name = _t("community.".concat(communityID, ".events.").concat(d.id, ".name"), {
95077 itemEnter.append('div').attr('class', 'community-event-when').html(function (d) {
95085 if (d.date.getHours() || d.date.getMinutes()) {
95086 // include time if it has one
95087 options.hour = 'numeric';
95088 options.minute = 'numeric';
95091 return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
95093 itemEnter.append('div').attr('class', 'community-event-where').html(function (d) {
95094 var where = d.where;
95096 if (d.i18n && d.id) {
95097 where = _t("community.".concat(communityID, ".events.").concat(d.id, ".where"), {
95104 itemEnter.append('div').attr('class', 'community-event-description').html(function (d) {
95105 var description = d.description;
95107 if (d.i18n && d.id) {
95108 description = _t("community.".concat(communityID, ".events.").concat(d.id, ".description"), {
95109 "default": description
95113 return description;
95117 function linkify(url, text) {
95118 text = text || url;
95119 return "<a target=\"_blank\" href=\"".concat(url, "\">").concat(text, "</a>");
95123 success.changeset = function (val) {
95124 if (!arguments.length) return _changeset;
95129 success.location = function (val) {
95130 if (!arguments.length) return _location;
95135 return utilRebind(success, dispatch$1, 'on');
95138 function modeSave(context) {
95142 var keybinding = utilKeybinding('modeSave');
95143 var commit = uiCommit(context).on('cancel', cancel);
95145 var _conflictsUi; // uiConflicts
95152 var uploader = context.uploader().on('saveStarted.modeSave', function () {
95154 }) // fire off some async work that we want to be ready later
95155 .on('willAttemptUpload.modeSave', prepareForSuccess).on('progressChanged.modeSave', showProgress).on('resultNoChanges.modeSave', function () {
95157 }).on('resultErrors.modeSave', showErrors).on('resultConflicts.modeSave', showConflicts).on('resultSuccess.modeSave', showSuccess);
95159 function cancel() {
95160 context.enter(modeBrowse(context));
95163 function showProgress(num, total) {
95164 var modal = context.container().select('.loading-modal .modal-section');
95165 var progress = modal.selectAll('.progress').data([0]); // enter/update
95167 progress.enter().append('div').attr('class', 'progress').merge(progress).text(_t('save.conflict_progress', {
95173 function showConflicts(changeset, conflicts, origChanges) {
95174 var selection = context.container().select('.sidebar').append('div').attr('class', 'sidebar-component');
95175 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
95176 _conflictsUi = uiConflicts(context).conflictList(conflicts).origChanges(origChanges).on('cancel', function () {
95177 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95178 selection.remove();
95180 uploader.cancelConflictResolution();
95181 }).on('save', function () {
95182 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95183 selection.remove();
95184 uploader.processResolvedConflicts(changeset);
95186 selection.call(_conflictsUi);
95189 function showErrors(errors) {
95191 var selection = uiConfirm(context.container());
95192 selection.select('.modal-section.header').append('h3').text(_t('save.error'));
95193 addErrors(selection, errors);
95194 selection.okButton();
95197 function addErrors(selection, data) {
95198 var message = selection.select('.modal-section.message-text');
95199 var items = message.selectAll('.error-container').data(data);
95200 var enter = items.enter().append('div').attr('class', 'error-container');
95201 enter.append('a').attr('class', 'error-description').attr('href', '#').classed('hide-toggle', true).text(function (d) {
95202 return d.msg || _t('save.unknown_error_details');
95203 }).on('click', function (d3_event) {
95204 d3_event.preventDefault();
95205 var error = select(this);
95206 var detail = select(this.nextElementSibling);
95207 var exp = error.classed('expanded');
95208 detail.style('display', exp ? 'none' : 'block');
95209 error.classed('expanded', !exp);
95211 var details = enter.append('div').attr('class', 'error-detail-container').style('display', 'none');
95212 details.append('ul').attr('class', 'error-detail-list').selectAll('li').data(function (d) {
95213 return d.details || [];
95214 }).enter().append('li').attr('class', 'error-detail-item').text(function (d) {
95217 items.exit().remove();
95220 function showSuccess(changeset) {
95223 var ui = _success.changeset(changeset).location(_location).on('cancel', function () {
95224 context.ui().sidebar.hide();
95227 context.enter(modeBrowse(context).sidebar(ui));
95230 function keybindingOn() {
95231 select(document).call(keybinding.on('⎋', cancel, true));
95234 function keybindingOff() {
95235 select(document).call(keybinding.unbind);
95236 } // Reverse geocode current map location so we can display a message on
95237 // the success screen like "Thank you for editing around place, region."
95240 function prepareForSuccess() {
95241 _success = uiSuccess(context);
95243 if (!services.geocoder) return;
95244 services.geocoder.reverse(context.map().center(), function (err, result) {
95245 if (err || !result || !result.address) return;
95246 var addr = result.address;
95247 var place = addr && (addr.town || addr.city || addr.county) || '';
95248 var region = addr && (addr.state || addr.country) || '';
95249 var separator = place && region ? _t('success.thank_you_where.separator') : '';
95250 _location = _t('success.thank_you_where.format', {
95252 separator: separator,
95258 mode.selectedIDs = function () {
95259 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
95262 mode.enter = function () {
95264 context.ui().sidebar.expand();
95267 context.ui().sidebar.show(commit);
95271 context.container().selectAll('.main-content').classed('active', false).classed('inactive', true);
95272 var osm = context.connection();
95279 if (osm.authenticated()) {
95282 osm.authenticate(function (err) {
95292 mode.exit = function () {
95294 context.container().selectAll('.main-content').classed('active', true).classed('inactive', false);
95295 context.ui().sidebar.hide();
95301 function uiToolOldDrawModes(context) {
95304 label: _t.html('toolbar.add_feature')
95306 var modes = [modeAddPoint(context, {
95307 title: _t.html('modes.add_point.title'),
95309 description: _t.html('modes.add_point.description'),
95310 preset: _mainPresetIndex.item('point'),
95312 }), modeAddLine(context, {
95313 title: _t.html('modes.add_line.title'),
95315 description: _t.html('modes.add_line.description'),
95316 preset: _mainPresetIndex.item('line'),
95318 }), modeAddArea(context, {
95319 title: _t.html('modes.add_area.title'),
95321 description: _t.html('modes.add_area.description'),
95322 preset: _mainPresetIndex.item('area'),
95326 function enabled() {
95327 return osmEditable();
95330 function osmEditable() {
95331 return context.editable();
95334 modes.forEach(function (mode) {
95335 context.keybinding().on(mode.key, function () {
95336 if (!enabled()) return;
95338 if (mode.id === context.mode().id) {
95339 context.enter(modeBrowse(context));
95341 context.enter(mode);
95346 tool.render = function (selection) {
95347 var wrap = selection.append('div').attr('class', 'joined').style('display', 'flex');
95349 var debouncedUpdate = debounce(update, 500, {
95354 context.map().on('move.modes', debouncedUpdate).on('drawn.modes', debouncedUpdate);
95355 context.on('enter.modes', update);
95358 function update() {
95359 var buttons = wrap.selectAll('button.add-button').data(modes, function (d) {
95363 buttons.exit().remove(); // enter
95365 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
95366 return d.id + ' add-button bar-button';
95367 }).on('click.mode-buttons', function (d3_event, d) {
95368 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
95370 var currMode = context.mode().id;
95371 if (/^draw/.test(currMode)) return;
95373 if (d.id === currMode) {
95374 context.enter(modeBrowse(context));
95378 }).call(uiTooltip().placement('bottom').title(function (d) {
95379 return d.description;
95380 }).keys(function (d) {
95382 }).scrollContainer(context.container().select('.top-toolbar')));
95383 buttonsEnter.each(function (d) {
95384 select(this).call(svgIcon('#iD-icon-' + d.button));
95386 buttonsEnter.append('span').attr('class', 'label').html(function (mode) {
95388 }); // if we are adding/removing the buttons, check if toolbar has overflowed
95390 if (buttons.enter().size() || buttons.exit().size()) {
95391 context.ui().checkOverflow('.top-toolbar', true);
95395 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
95397 }).classed('active', function (d) {
95398 return context.mode() && context.mode().button === d.button;
95406 function uiToolNotes(context) {
95409 label: _t.html('modes.add_note.label')
95411 var mode = modeAddNote(context);
95413 function enabled() {
95414 return notesEnabled() && notesEditable();
95417 function notesEnabled() {
95418 var noteLayer = context.layers().layer('notes');
95419 return noteLayer && noteLayer.enabled();
95422 function notesEditable() {
95423 var mode = context.mode();
95424 return context.map().notesEditable() && mode && mode.id !== 'save';
95427 context.keybinding().on(mode.key, function () {
95428 if (!enabled()) return;
95430 if (mode.id === context.mode().id) {
95431 context.enter(modeBrowse(context));
95433 context.enter(mode);
95437 tool.render = function (selection) {
95438 var debouncedUpdate = debounce(update, 500, {
95443 context.map().on('move.notes', debouncedUpdate).on('drawn.notes', debouncedUpdate);
95444 context.on('enter.notes', update);
95447 function update() {
95448 var showNotes = notesEnabled();
95449 var data = showNotes ? [mode] : [];
95450 var buttons = selection.selectAll('button.add-button').data(data, function (d) {
95454 buttons.exit().remove(); // enter
95456 var buttonsEnter = buttons.enter().append('button').attr('class', function (d) {
95457 return d.id + ' add-button bar-button';
95458 }).on('click.notes', function (d3_event, d) {
95459 if (!enabled()) return; // When drawing, ignore accidental clicks on mode buttons - #4042
95461 var currMode = context.mode().id;
95462 if (/^draw/.test(currMode)) return;
95464 if (d.id === currMode) {
95465 context.enter(modeBrowse(context));
95469 }).call(uiTooltip().placement('bottom').title(function (d) {
95470 return d.description;
95471 }).keys(function (d) {
95473 }).scrollContainer(context.container().select('.top-toolbar')));
95474 buttonsEnter.each(function (d) {
95475 select(this).call(svgIcon(d.icon || '#iD-icon-' + d.button));
95476 }); // if we are adding/removing the buttons, check if toolbar has overflowed
95478 if (buttons.enter().size() || buttons.exit().size()) {
95479 context.ui().checkOverflow('.top-toolbar', true);
95483 buttons = buttons.merge(buttonsEnter).classed('disabled', function (d) {
95485 }).classed('active', function (d) {
95486 return context.mode() && context.mode().button === d.button;
95491 tool.uninstall = function () {
95492 context.on('enter.editor.notes', null).on('exit.editor.notes', null).on('enter.notes', null);
95493 context.map().on('move.notes', null).on('drawn.notes', null);
95499 function uiToolSave(context) {
95502 label: _t.html('save.title')
95505 var tooltipBehavior = null;
95506 var history = context.history();
95507 var key = uiCmd('⌘S');
95508 var _numChanges = 0;
95510 function isSaving() {
95511 var mode = context.mode();
95512 return mode && mode.id === 'save';
95515 function isDisabled() {
95516 return _numChanges === 0 || isSaving();
95519 function save(d3_event) {
95520 d3_event.preventDefault();
95522 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
95523 context.enter(modeSave(context));
95527 function bgColor() {
95530 if (_numChanges === 0) {
95532 } else if (_numChanges <= 50) {
95533 step = _numChanges / 50;
95534 return d3_interpolateRgb('#fff', '#ff8')(step); // white -> yellow
95536 step = Math.min((_numChanges - 50) / 50, 1.0);
95537 return d3_interpolateRgb('#ff8', '#f88')(step); // yellow -> red
95541 function updateCount() {
95542 var val = history.difference().summary().length;
95543 if (val === _numChanges) return;
95546 if (tooltipBehavior) {
95547 tooltipBehavior.title(_t.html(_numChanges > 0 ? 'save.help' : 'save.no_changes')).keys([key]);
95551 button.classed('disabled', isDisabled()).style('background', bgColor());
95552 button.select('span.count').html(_numChanges);
95556 tool.render = function (selection) {
95557 tooltipBehavior = uiTooltip().placement('bottom').title(_t.html('save.no_changes')).keys([key]).scrollContainer(context.container().select('.top-toolbar'));
95558 var lastPointerUpType;
95559 button = selection.append('button').attr('class', 'save disabled bar-button').on('pointerup', function (d3_event) {
95560 lastPointerUpType = d3_event.pointerType;
95561 }).on('click', function (d3_event) {
95564 if (_numChanges === 0 && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
95565 // there are no tooltips for touch interactions so flash feedback instead
95566 context.ui().flash.duration(2000).iconName('#iD-icon-save').iconClass('disabled').label(_t.html('save.no_changes'))();
95569 lastPointerUpType = null;
95570 }).call(tooltipBehavior);
95571 button.call(svgIcon('#iD-icon-save'));
95572 button.append('span').attr('class', 'count').attr('aria-hidden', 'true').html('0');
95574 context.keybinding().on(key, save, true);
95575 context.history().on('change.save', updateCount);
95576 context.on('enter.save', function () {
95578 button.classed('disabled', isDisabled());
95581 button.call(tooltipBehavior.hide);
95587 tool.uninstall = function () {
95588 context.keybinding().off(key, true);
95589 context.history().on('change.save', null);
95590 context.on('enter.save', null);
95592 tooltipBehavior = null;
95598 function uiToolSidebarToggle(context) {
95600 id: 'sidebar_toggle',
95601 label: _t.html('toolbar.inspect')
95604 tool.render = function (selection) {
95605 selection.append('button').attr('class', 'bar-button').on('click', function () {
95606 context.ui().sidebar.toggle();
95607 }).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')));
95613 function uiToolUndoRedo(context) {
95616 label: _t.html('toolbar.undo_redo')
95621 action: function action() {
95624 annotation: function annotation() {
95625 return context.history().undoAnnotation();
95627 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
95631 action: function action() {
95634 annotation: function annotation() {
95635 return context.history().redoAnnotation();
95637 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
95640 function editable() {
95641 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true
95642 /* ignore min zoom */
95646 tool.render = function (selection) {
95647 var tooltipBehavior = uiTooltip().placement('bottom').title(function (d) {
95648 return d.annotation() ? _t.html(d.id + '.tooltip', {
95649 action: d.annotation()
95650 }) : _t.html(d.id + '.nothing');
95651 }).keys(function (d) {
95653 }).scrollContainer(context.container().select('.top-toolbar'));
95654 var lastPointerUpType;
95655 var buttons = selection.selectAll('button').data(commands).enter().append('button').attr('class', function (d) {
95656 return 'disabled ' + d.id + '-button bar-button';
95657 }).on('pointerup', function (d3_event) {
95658 // `pointerup` is always called before `click`
95659 lastPointerUpType = d3_event.pointerType;
95660 }).on('click', function (d3_event, d) {
95661 d3_event.preventDefault();
95662 var annotation = d.annotation();
95664 if (editable() && annotation) {
95668 if (editable() && (lastPointerUpType === 'touch' || lastPointerUpType === 'pen')) {
95669 // there are no tooltips for touch interactions so flash feedback instead
95670 var text = annotation ? _t(d.id + '.tooltip', {
95672 }) : _t(d.id + '.nothing');
95673 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass(annotation ? '' : 'disabled').label(text)();
95676 lastPointerUpType = null;
95677 }).call(tooltipBehavior);
95678 buttons.each(function (d) {
95679 select(this).call(svgIcon('#' + d.icon));
95681 context.keybinding().on(commands[0].cmd, function (d3_event) {
95682 d3_event.preventDefault();
95683 if (editable()) commands[0].action();
95684 }).on(commands[1].cmd, function (d3_event) {
95685 d3_event.preventDefault();
95686 if (editable()) commands[1].action();
95689 var debouncedUpdate = debounce(update, 500, {
95694 context.map().on('move.undo_redo', debouncedUpdate).on('drawn.undo_redo', debouncedUpdate);
95695 context.history().on('change.undo_redo', function (difference) {
95696 if (difference) update();
95698 context.on('enter.undo_redo', update);
95700 function update() {
95701 buttons.classed('disabled', function (d) {
95702 return !editable() || !d.annotation();
95703 }).each(function () {
95704 var selection = select(this);
95706 if (!selection.select('.tooltip.in').empty()) {
95707 selection.call(tooltipBehavior.updateContent);
95713 tool.uninstall = function () {
95714 context.keybinding().off(commands[0].cmd).off(commands[1].cmd);
95715 context.map().on('move.undo_redo', null).on('drawn.undo_redo', null);
95716 context.history().on('change.undo_redo', null);
95717 context.on('enter.undo_redo', null);
95723 function uiTopToolbar(context) {
95724 var sidebarToggle = uiToolSidebarToggle(context),
95725 modes = uiToolOldDrawModes(context),
95726 notes = uiToolNotes(context),
95727 undoRedo = uiToolUndoRedo(context),
95728 save = uiToolSave(context);
95730 function notesEnabled() {
95731 var noteLayer = context.layers().layer('notes');
95732 return noteLayer && noteLayer.enabled();
95735 function topToolbar(bar) {
95736 bar.on('wheel.topToolbar', function (d3_event) {
95737 if (!d3_event.deltaX) {
95738 // translate vertical scrolling into horizontal scrolling in case
95739 // the user doesn't have an input device that can scroll horizontally
95740 bar.node().scrollLeft += d3_event.deltaY;
95744 var debouncedUpdate = debounce(update, 500, {
95749 context.layers().on('change.topToolbar', debouncedUpdate);
95752 function update() {
95753 var tools = [sidebarToggle, 'spacer', modes];
95754 tools.push('spacer');
95756 if (notesEnabled()) {
95757 tools = tools.concat([notes, 'spacer']);
95760 tools = tools.concat([undoRedo, save]);
95761 var toolbarItems = bar.selectAll('.toolbar-item').data(tools, function (d) {
95764 toolbarItems.exit().each(function (d) {
95769 var itemsEnter = toolbarItems.enter().append('div').attr('class', function (d) {
95770 var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
95771 if (d.klass) classes += ' ' + d.klass;
95774 var actionableItems = itemsEnter.filter(function (d) {
95775 return d !== 'spacer';
95777 actionableItems.append('div').attr('class', 'item-content').each(function (d) {
95778 select(this).call(d.render, bar);
95780 actionableItems.append('div').attr('class', 'item-label').html(function (d) {
95789 var sawVersion = null;
95790 var isNewVersion = false;
95791 var isNewUser = false;
95792 function uiVersion(context) {
95793 var currVersion = context.version;
95794 var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
95796 if (sawVersion === null && matchedVersion !== null) {
95797 if (corePreferences('sawVersion')) {
95799 isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
95802 isNewVersion = true;
95805 corePreferences('sawVersion', currVersion);
95806 sawVersion = currVersion;
95809 return function (selection) {
95810 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
95812 if (isNewVersion && !isNewUser) {
95813 selection.append('a').attr('class', 'badge').attr('target', '_blank').attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new').call(svgIcon('#maki-gift-11')).call(uiTooltip().title(_t.html('version.whats_new', {
95814 version: currVersion
95815 })).placement('top').scrollContainer(context.container().select('.main-footer-wrap')));
95820 function uiZoom(context) {
95823 icon: 'iD-icon-plus',
95824 title: _t.html('zoom.in'),
95826 disabled: function disabled() {
95827 return !context.map().canZoomIn();
95829 disabledTitle: _t.html('zoom.disabled.in'),
95833 icon: 'iD-icon-minus',
95834 title: _t.html('zoom.out'),
95836 disabled: function disabled() {
95837 return !context.map().canZoomOut();
95839 disabledTitle: _t.html('zoom.disabled.out'),
95843 function zoomIn(d3_event) {
95844 if (d3_event.shiftKey) return;
95845 d3_event.preventDefault();
95846 context.map().zoomIn();
95849 function zoomOut(d3_event) {
95850 if (d3_event.shiftKey) return;
95851 d3_event.preventDefault();
95852 context.map().zoomOut();
95855 function zoomInFurther(d3_event) {
95856 if (d3_event.shiftKey) return;
95857 d3_event.preventDefault();
95858 context.map().zoomInFurther();
95861 function zoomOutFurther(d3_event) {
95862 if (d3_event.shiftKey) return;
95863 d3_event.preventDefault();
95864 context.map().zoomOutFurther();
95867 return function (selection) {
95868 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function (d) {
95869 if (d.disabled()) {
95870 return d.disabledTitle;
95874 }).keys(function (d) {
95877 var lastPointerUpType;
95878 var buttons = selection.selectAll('button').data(zooms).enter().append('button').attr('class', function (d) {
95880 }).on('pointerup.editor', function (d3_event) {
95881 lastPointerUpType = d3_event.pointerType;
95882 }).on('click.editor', function (d3_event, d) {
95883 if (!d.disabled()) {
95884 d.action(d3_event);
95885 } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
95886 context.ui().flash.duration(2000).iconName('#' + d.icon).iconClass('disabled').label(d.disabledTitle)();
95889 lastPointerUpType = null;
95890 }).call(tooltipBehavior);
95891 buttons.each(function (d) {
95892 select(this).call(svgIcon('#' + d.icon, 'light'));
95894 utilKeybinding.plusKeys.forEach(function (key) {
95895 context.keybinding().on([key], zoomIn);
95896 context.keybinding().on([uiCmd('⌥' + key)], zoomInFurther);
95898 utilKeybinding.minusKeys.forEach(function (key) {
95899 context.keybinding().on([key], zoomOut);
95900 context.keybinding().on([uiCmd('⌥' + key)], zoomOutFurther);
95903 function updateButtonStates() {
95904 buttons.classed('disabled', function (d) {
95905 return d.disabled();
95906 }).each(function () {
95907 var selection = select(this);
95909 if (!selection.select('.tooltip.in').empty()) {
95910 selection.call(tooltipBehavior.updateContent);
95915 updateButtonStates();
95916 context.map().on('move.uiZoom', updateButtonStates);
95920 function uiZoomToSelection(context) {
95921 function isDisabled() {
95922 var mode = context.mode();
95923 return !mode || !mode.zoomToSelected;
95926 var _lastPointerUpType;
95928 function pointerup(d3_event) {
95929 _lastPointerUpType = d3_event.pointerType;
95932 function click(d3_event) {
95933 d3_event.preventDefault();
95935 if (isDisabled()) {
95936 if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
95937 context.ui().flash.duration(2000).iconName('#iD-icon-framed-dot').iconClass('disabled').label(_t.html('inspector.zoom_to.no_selection'))();
95940 var mode = context.mode();
95942 if (mode && mode.zoomToSelected) {
95943 mode.zoomToSelected();
95947 _lastPointerUpType = null;
95950 return function (selection) {
95951 var tooltipBehavior = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(function () {
95952 if (isDisabled()) {
95953 return _t.html('inspector.zoom_to.no_selection');
95956 return _t.html('inspector.zoom_to.title');
95957 }).keys([_t('inspector.zoom_to.key')]);
95958 var button = selection.append('button').on('pointerup', pointerup).on('click', click).call(svgIcon('#iD-icon-framed-dot', 'light')).call(tooltipBehavior);
95960 function setEnabledState() {
95961 button.classed('disabled', isDisabled());
95963 if (!button.select('.tooltip.in').empty()) {
95964 button.call(tooltipBehavior.updateContent);
95968 context.on('enter.uiZoomToSelection', setEnabledState);
95973 function uiPane(id, context) {
95977 var _description = '';
95978 var _iconName = '';
95980 var _sections; // array of uiSection objects
95983 var _paneSelection = select(null);
95991 pane.label = function (val) {
95992 if (!arguments.length) return _label;
95997 pane.key = function (val) {
95998 if (!arguments.length) return _key;
96003 pane.description = function (val) {
96004 if (!arguments.length) return _description;
96005 _description = val;
96009 pane.iconName = function (val) {
96010 if (!arguments.length) return _iconName;
96015 pane.sections = function (val) {
96016 if (!arguments.length) return _sections;
96021 pane.selection = function () {
96022 return _paneSelection;
96025 function hidePane() {
96026 context.ui().togglePanes();
96029 pane.togglePane = function (d3_event) {
96030 if (d3_event) d3_event.preventDefault();
96032 _paneTooltip.hide();
96034 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
96037 pane.renderToggleButton = function (selection) {
96038 if (!_paneTooltip) {
96039 _paneTooltip = uiTooltip().placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left').title(_description).keys([_key]);
96042 selection.append('button').on('click', pane.togglePane).call(svgIcon('#' + _iconName, 'light')).call(_paneTooltip);
96045 pane.renderContent = function (selection) {
96046 // override to fully customize content
96048 _sections.forEach(function (section) {
96049 selection.call(section.render);
96054 pane.renderPane = function (selection) {
96055 _paneSelection = selection.append('div').attr('class', 'fillL map-pane hide ' + id + '-pane').attr('pane', id);
96057 var heading = _paneSelection.append('div').attr('class', 'pane-heading');
96059 heading.append('h2').html(_label);
96060 heading.append('button').on('click', hidePane).call(svgIcon('#iD-icon-close'));
96062 _paneSelection.append('div').attr('class', 'pane-content').call(pane.renderContent);
96065 context.keybinding().on(_key, pane.togglePane);
96072 function uiSectionBackgroundDisplayOptions(context) {
96073 var section = uiSection('background-display-options', context).label(_t.html('background.display_options')).disclosureContent(renderDisclosureContent);
96075 var _detected = utilDetect();
96077 var _storedOpacity = corePreferences('background-opacity');
96081 var _maxVal = _detected.cssfilters ? 3 : 1;
96083 var _sliders = _detected.cssfilters ? ['brightness', 'contrast', 'saturation', 'sharpness'] : ['brightness'];
96086 brightness: _storedOpacity !== null ? +_storedOpacity : 1,
96092 function clamp(x, min, max) {
96093 return Math.max(min, Math.min(x, max));
96096 function updateValue(d, val) {
96097 val = clamp(val, _minVal, _maxVal);
96099 context.background()[d](val);
96101 if (d === 'brightness') {
96102 corePreferences('background-opacity', val);
96105 section.reRender();
96108 function renderDisclosureContent(selection) {
96109 var container = selection.selectAll('.display-options-container').data([0]);
96110 var containerEnter = container.enter().append('div').attr('class', 'display-options-container controls-list'); // add slider controls
96112 var slidersEnter = containerEnter.selectAll('.display-control').data(_sliders).enter().append('div').attr('class', function (d) {
96113 return 'display-control display-control-' + d;
96115 slidersEnter.append('h5').html(function (d) {
96116 return _t.html('background.' + d);
96117 }).append('span').attr('class', function (d) {
96118 return 'display-option-value display-option-value-' + d;
96120 var sildersControlEnter = slidersEnter.append('div').attr('class', 'control-wrap');
96121 sildersControlEnter.append('input').attr('class', function (d) {
96122 return 'display-option-input display-option-input-' + d;
96123 }).attr('type', 'range').attr('min', _minVal).attr('max', _maxVal).attr('step', '0.05').on('input', function (d3_event, d) {
96124 var val = select(this).property('value');
96126 if (!val && d3_event && d3_event.target) {
96127 val = d3_event.target.value;
96130 updateValue(d, val);
96132 sildersControlEnter.append('button').attr('title', _t('background.reset')).attr('class', function (d) {
96133 return 'display-option-reset display-option-reset-' + d;
96134 }).on('click', function (d3_event, d) {
96135 if (d3_event.button !== 0) return;
96137 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo'))); // reset all button
96139 containerEnter.append('a').attr('class', 'display-option-resetlink').attr('href', '#').html(_t.html('background.reset_all')).on('click', function (d3_event) {
96140 d3_event.preventDefault();
96142 for (var i = 0; i < _sliders.length; i++) {
96143 updateValue(_sliders[i], 1);
96147 container = containerEnter.merge(container);
96148 container.selectAll('.display-option-input').property('value', function (d) {
96149 return _options[d];
96151 container.selectAll('.display-option-value').html(function (d) {
96152 return Math.floor(_options[d] * 100) + '%';
96154 container.selectAll('.display-option-reset').classed('disabled', function (d) {
96155 return _options[d] === 1;
96156 }); // first time only, set brightness if needed
96158 if (containerEnter.size() && _options.brightness !== 1) {
96159 context.background().brightness(_options.brightness);
96166 function uiSettingsCustomBackground() {
96167 var dispatch$1 = dispatch('change');
96169 function render(selection) {
96170 // keep separate copies of original and current settings
96171 var _origSettings = {
96172 template: corePreferences('background-custom-template')
96174 var _currSettings = {
96175 template: corePreferences('background-custom-template')
96177 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
96178 var modal = uiConfirm(selection).okButton();
96179 modal.classed('settings-modal settings-custom-background', true);
96180 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_background.header'));
96181 var textSection = modal.select('.modal-section.message-text');
96182 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, "`");
96183 textSection.append('div').attr('class', 'instructions-template').html(marked_1(instructions));
96184 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
96186 var buttonSection = modal.select('.modal-section.buttons');
96187 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
96188 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
96189 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
96191 function isSaveDisabled() {
96193 } // restore the original template
96196 function clickCancel() {
96197 textSection.select('.field-template').property('value', _origSettings.template);
96198 corePreferences('background-custom-template', _origSettings.template);
96201 } // accept the current template
96204 function clickSave() {
96205 _currSettings.template = textSection.select('.field-template').property('value');
96206 corePreferences('background-custom-template', _currSettings.template);
96209 dispatch$1.call('change', this, _currSettings);
96213 return utilRebind(render, dispatch$1, 'on');
96216 function uiSectionBackgroundList(context) {
96217 var _backgroundList = select(null);
96219 var _customSource = context.background().findSource('custom');
96221 var _settingsCustomBackground = uiSettingsCustomBackground().on('change', customChanged);
96223 var section = uiSection('background-list', context).label(_t.html('background.backgrounds')).disclosureContent(renderDisclosureContent);
96225 function previousBackgroundID() {
96226 return corePreferences('background-last-used-toggle');
96229 function renderDisclosureContent(selection) {
96230 // the background list
96231 var container = selection.selectAll('.layer-background-list').data([0]);
96232 _backgroundList = container.enter().append('ul').attr('class', 'layer-list layer-background-list').attr('dir', 'auto').merge(container); // add minimap toggle below list
96234 var bgExtrasListEnter = selection.selectAll('.bg-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list bg-extras-list');
96235 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'));
96236 minimapLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96237 d3_event.preventDefault();
96238 uiMapInMap.toggle();
96240 minimapLabelEnter.append('span').html(_t.html('background.minimap.description'));
96241 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'));
96242 panelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96243 d3_event.preventDefault();
96244 context.ui().info.toggle('background');
96246 panelLabelEnter.append('span').html(_t.html('background.panel.description'));
96247 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'));
96248 locPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
96249 d3_event.preventDefault();
96250 context.ui().info.toggle('location');
96252 locPanelLabelEnter.append('span').html(_t.html('background.location_panel.description')); // "Info / Report a Problem" link
96254 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'));
96256 _backgroundList.call(drawListItems, 'radio', function (d3_event, d) {
96257 chooseBackground(d);
96259 return !d.isHidden() && !d.overlay;
96263 function setTooltips(selection) {
96264 selection.each(function (d, i, nodes) {
96265 var item = select(this).select('label');
96266 var span = item.select('span');
96267 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
96268 var description = d.description();
96269 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
96270 item.call(uiTooltip().destroyAny);
96272 if (d.id === previousBackgroundID()) {
96273 item.call(uiTooltip().placement(placement).title('<div>' + _t.html('background.switch') + '</div>').keys([uiCmd('⌘' + _t('background.key'))]));
96274 } else if (description || isOverflowing) {
96275 item.call(uiTooltip().placement(placement).title(description || d.label()));
96280 function drawListItems(layerList, type, change, filter) {
96281 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter).sort(function (a, b) {
96282 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
96284 var layerLinks = layerList.selectAll('li') // We have to be a bit inefficient about reordering the list since
96285 // arrow key navigation of radio values likes to work in the order
96286 // they were added, not the display document order.
96287 .data(sources, function (d, i) {
96288 return d.id + '---' + i;
96290 layerLinks.exit().remove();
96291 var enter = layerLinks.enter().append('li').classed('layer-custom', function (d) {
96292 return d.id === 'custom';
96293 }).classed('best', function (d) {
96296 var label = enter.append('label');
96297 label.append('input').attr('type', type).attr('name', 'background-layer').attr('value', function (d) {
96299 }).on('change', change);
96300 label.append('span').html(function (d) {
96303 enter.filter(function (d) {
96304 return d.id === 'custom';
96305 }).append('button').attr('class', 'layer-browse').call(uiTooltip().title(_t.html('settings.custom_background.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
96306 d3_event.preventDefault();
96308 }).call(svgIcon('#iD-icon-more'));
96309 enter.filter(function (d) {
96311 }).append('div').attr('class', 'best').call(uiTooltip().title(_t.html('background.best_imagery')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).append('span').html('★');
96312 layerList.call(updateLayerSelections);
96315 function updateLayerSelections(selection) {
96316 function active(d) {
96317 return context.background().showsLayer(d);
96320 selection.selectAll('li').classed('active', active).classed('switch', function (d) {
96321 return d.id === previousBackgroundID();
96322 }).call(setTooltips).selectAll('input').property('checked', active);
96325 function chooseBackground(d) {
96326 if (d.id === 'custom' && !d.template()) {
96327 return editCustom();
96330 var previousBackground = context.background().baseLayerSource();
96331 corePreferences('background-last-used-toggle', previousBackground.id);
96332 corePreferences('background-last-used', d.id);
96333 context.background().baseLayerSource(d);
96336 function customChanged(d) {
96337 if (d && d.template) {
96338 _customSource.template(d.template);
96340 chooseBackground(_customSource);
96342 _customSource.template('');
96344 chooseBackground(context.background().findSource('none'));
96348 function editCustom() {
96349 context.container().call(_settingsCustomBackground);
96352 context.background().on('change.background_list', function () {
96353 _backgroundList.call(updateLayerSelections);
96355 context.map().on('move.background_list', debounce(function () {
96356 // layers in-view may have changed due to map move
96357 window.requestIdleCallback(section.reRender);
96362 function uiSectionBackgroundOffset(context) {
96363 var section = uiSection('background-offset', context).label(_t.html('background.fix_misalignment')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
96365 var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
96367 var _directions = [['top', [0, -0.5]], ['left', [-0.5, 0]], ['right', [0.5, 0]], ['bottom', [0, 0.5]]];
96369 function updateValue() {
96370 var meters = geoOffsetToMeters(context.background().offset());
96371 var x = +meters[0].toFixed(2);
96372 var y = +meters[1].toFixed(2);
96373 context.container().selectAll('.nudge-inner-rect').select('input').classed('error', false).property('value', x + ', ' + y);
96374 context.container().selectAll('.nudge-reset').classed('disabled', function () {
96375 return x === 0 && y === 0;
96379 function resetOffset() {
96380 context.background().offset([0, 0]);
96384 function nudge(d) {
96385 context.background().nudge(d, context.map().zoom());
96389 function inputOffset() {
96390 var input = select(this);
96391 var d = input.node().value;
96392 if (d === '') return resetOffset();
96393 d = d.replace(/;/g, ',').split(',').map(function (n) {
96394 // if n is NaN, it will always get mapped to false.
96395 return !isNaN(n) && n;
96398 if (d.length !== 2 || !d[0] || !d[1]) {
96399 input.classed('error', true);
96403 context.background().offset(geoMetersToOffset(d));
96407 function dragOffset(d3_event) {
96408 if (d3_event.button !== 0) return;
96409 var origin = [d3_event.clientX, d3_event.clientY];
96410 var pointerId = d3_event.pointerId || 'mouse';
96411 context.container().append('div').attr('class', 'nudge-surface');
96412 select(window).on(_pointerPrefix + 'move.drag-bg-offset', pointermove).on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
96414 if (_pointerPrefix === 'pointer') {
96415 select(window).on('pointercancel.drag-bg-offset', pointerup);
96418 function pointermove(d3_event) {
96419 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
96420 var latest = [d3_event.clientX, d3_event.clientY];
96421 var d = [-(origin[0] - latest[0]) / 4, -(origin[1] - latest[1]) / 4];
96426 function pointerup(d3_event) {
96427 if (pointerId !== (d3_event.pointerId || 'mouse')) return;
96428 if (d3_event.button !== 0) return;
96429 context.container().selectAll('.nudge-surface').remove();
96430 select(window).on('.drag-bg-offset', null);
96434 function renderDisclosureContent(selection) {
96435 var container = selection.selectAll('.nudge-container').data([0]);
96436 var containerEnter = container.enter().append('div').attr('class', 'nudge-container');
96437 containerEnter.append('div').attr('class', 'nudge-instructions').html(_t.html('background.offset'));
96438 var nudgeWrapEnter = containerEnter.append('div').attr('class', 'nudge-controls-wrap');
96439 var nudgeEnter = nudgeWrapEnter.append('div').attr('class', 'nudge-outer-rect').on(_pointerPrefix + 'down', dragOffset);
96440 nudgeEnter.append('div').attr('class', 'nudge-inner-rect').append('input').attr('type', 'text').on('change', inputOffset);
96441 nudgeWrapEnter.append('div').selectAll('button').data(_directions).enter().append('button').attr('class', function (d) {
96442 return d[0] + ' nudge';
96443 }).on('click', function (d3_event, d) {
96446 nudgeWrapEnter.append('button').attr('title', _t('background.reset')).attr('class', 'nudge-reset disabled').on('click', function (d3_event) {
96447 d3_event.preventDefault();
96449 }).call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
96453 context.background().on('change.backgroundOffset-update', updateValue);
96457 function uiSectionOverlayList(context) {
96458 var section = uiSection('overlay-list', context).label(_t.html('background.overlays')).disclosureContent(renderDisclosureContent);
96460 var _overlayList = select(null);
96462 function setTooltips(selection) {
96463 selection.each(function (d, i, nodes) {
96464 var item = select(this).select('label');
96465 var span = item.select('span');
96466 var placement = i < nodes.length / 2 ? 'bottom' : 'top';
96467 var description = d.description();
96468 var isOverflowing = span.property('clientWidth') !== span.property('scrollWidth');
96469 item.call(uiTooltip().destroyAny);
96471 if (description || isOverflowing) {
96472 item.call(uiTooltip().placement(placement).title(description || d.name()));
96477 function updateLayerSelections(selection) {
96478 function active(d) {
96479 return context.background().showsLayer(d);
96482 selection.selectAll('li').classed('active', active).call(setTooltips).selectAll('input').property('checked', active);
96485 function chooseOverlay(d3_event, d) {
96486 d3_event.preventDefault();
96487 context.background().toggleOverlayLayer(d);
96489 _overlayList.call(updateLayerSelections);
96491 document.activeElement.blur();
96494 function drawListItems(layerList, type, change, filter) {
96495 var sources = context.background().sources(context.map().extent(), context.map().zoom(), true).filter(filter);
96496 var layerLinks = layerList.selectAll('li').data(sources, function (d) {
96499 layerLinks.exit().remove();
96500 var enter = layerLinks.enter().append('li');
96501 var label = enter.append('label');
96502 label.append('input').attr('type', type).attr('name', 'layers').on('change', change);
96503 label.append('span').html(function (d) {
96506 layerList.selectAll('li').sort(sortSources);
96507 layerList.call(updateLayerSelections);
96509 function sortSources(a, b) {
96510 return a.best() && !b.best() ? -1 : b.best() && !a.best() ? 1 : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
96514 function renderDisclosureContent(selection) {
96515 var container = selection.selectAll('.layer-overlay-list').data([0]);
96516 _overlayList = container.enter().append('ul').attr('class', 'layer-list layer-overlay-list').attr('dir', 'auto').merge(container);
96518 _overlayList.call(drawListItems, 'checkbox', chooseOverlay, function (d) {
96519 return !d.isHidden() && d.overlay;
96523 context.map().on('move.overlay_list', debounce(function () {
96524 // layers in-view may have changed due to map move
96525 window.requestIdleCallback(section.reRender);
96530 function uiPaneBackground(context) {
96531 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)]);
96532 return backgroundPane;
96535 function uiPaneHelp(context) {
96536 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']]];
96538 'help.help.open_data_h': 3,
96539 'help.help.before_start_h': 3,
96540 'help.help.open_source_h': 3,
96541 'help.overview.navigation_h': 3,
96542 'help.overview.features_h': 3,
96543 'help.editing.select_h': 3,
96544 'help.editing.multiselect_h': 3,
96545 'help.editing.undo_redo_h': 3,
96546 'help.editing.save_h': 3,
96547 'help.editing.upload_h': 3,
96548 'help.editing.backups_h': 3,
96549 'help.editing.keyboard_h': 3,
96550 'help.feature_editor.type_h': 3,
96551 'help.feature_editor.fields_h': 3,
96552 'help.feature_editor.tags_h': 3,
96553 'help.points.add_point_h': 3,
96554 'help.points.move_point_h': 3,
96555 'help.points.delete_point_h': 3,
96556 'help.lines.add_line_h': 3,
96557 'help.lines.modify_line_h': 3,
96558 'help.lines.connect_line_h': 3,
96559 'help.lines.disconnect_line_h': 3,
96560 'help.lines.move_line_h': 3,
96561 'help.lines.delete_line_h': 3,
96562 'help.areas.point_or_area_h': 3,
96563 'help.areas.add_area_h': 3,
96564 'help.areas.square_area_h': 3,
96565 'help.areas.modify_area_h': 3,
96566 'help.areas.delete_area_h': 3,
96567 'help.relations.edit_relation_h': 3,
96568 'help.relations.maintain_relation_h': 3,
96569 'help.relations.relation_types_h': 2,
96570 'help.relations.multipolygon_h': 3,
96571 'help.relations.turn_restriction_h': 3,
96572 'help.relations.route_h': 3,
96573 'help.relations.boundary_h': 3,
96574 'help.notes.add_note_h': 3,
96575 'help.notes.update_note_h': 3,
96576 'help.notes.save_note_h': 3,
96577 'help.imagery.sources_h': 3,
96578 'help.imagery.offsets_h': 3,
96579 'help.streetlevel.using_h': 3,
96580 'help.gps.using_h': 3,
96581 'help.qa.tools_h': 3,
96582 'help.qa.issues_h': 3
96583 }; // For each section, squash all the texts into a single markdown document
96585 var docs = docKeys.map(function (key) {
96586 var helpkey = 'help.' + key[0];
96587 var helpPaneReplacements = {
96588 version: context.version
96590 var text = key[1].reduce(function (all, part) {
96591 var subkey = helpkey + '.' + part;
96592 var depth = headings[subkey]; // is this subkey a heading?
96594 var hhh = depth ? Array(depth + 1).join('#') + ' ' : ''; // if so, prepend with some ##'s
96596 return all + hhh + helpHtml(subkey, helpPaneReplacements) + '\n\n';
96599 title: _t.html(helpkey + '.title'),
96600 content: marked_1(text.trim()) // use keyboard key styling for shortcuts
96601 .replace(/<code>/g, '<kbd>').replace(/<\/code>/g, '<\/kbd>')
96604 var helpPane = uiPane('help', context).key(_t('help.key')).label(_t.html('help.title')).description(_t.html('help.title')).iconName('iD-icon-help');
96606 helpPane.renderContent = function (content) {
96607 function clickHelp(d, i) {
96608 var rtl = _mainLocalizer.textDirection() === 'rtl';
96609 content.property('scrollTop', 0);
96610 helpPane.selection().select('.pane-heading h2').html(d.title);
96611 body.html(d.content);
96612 body.selectAll('a').attr('target', '_blank');
96613 menuItems.classed('selected', function (m) {
96614 return m.title === d.title;
96619 nav.call(drawNext).call(drawPrevious);
96621 nav.call(drawPrevious).call(drawNext);
96624 function drawNext(selection) {
96625 if (i < docs.length - 1) {
96626 var nextLink = selection.append('a').attr('href', '#').attr('class', 'next').on('click', function (d3_event) {
96627 d3_event.preventDefault();
96628 clickHelp(docs[i + 1], i + 1);
96630 nextLink.append('span').html(docs[i + 1].title).call(svgIcon(rtl ? '#iD-icon-backward' : '#iD-icon-forward', 'inline'));
96634 function drawPrevious(selection) {
96636 var prevLink = selection.append('a').attr('href', '#').attr('class', 'previous').on('click', function (d3_event) {
96637 d3_event.preventDefault();
96638 clickHelp(docs[i - 1], i - 1);
96640 prevLink.call(svgIcon(rtl ? '#iD-icon-forward' : '#iD-icon-backward', 'inline')).append('span').html(docs[i - 1].title);
96645 function clickWalkthrough(d3_event) {
96646 d3_event.preventDefault();
96647 if (context.inIntro()) return;
96648 context.container().call(uiIntro(context));
96649 context.ui().togglePanes();
96652 function clickShortcuts(d3_event) {
96653 d3_event.preventDefault();
96654 context.container().call(context.ui().shortcuts, true);
96657 var toc = content.append('ul').attr('class', 'toc');
96658 var menuItems = toc.selectAll('li').data(docs).enter().append('li').append('a').attr('href', '#').html(function (d) {
96660 }).on('click', function (d3_event, d) {
96661 d3_event.preventDefault();
96662 clickHelp(d, docs.indexOf(d));
96664 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);
96665 shortcuts.append('div').html(_t.html('shortcuts.title'));
96666 var walkthrough = toc.append('li').attr('class', 'walkthrough').append('a').attr('href', '#').on('click', clickWalkthrough);
96667 walkthrough.append('svg').attr('class', 'logo logo-walkthrough').append('use').attr('xlink:href', '#iD-logo-walkthrough');
96668 walkthrough.append('div').html(_t.html('splash.walkthrough'));
96669 var helpContent = content.append('div').attr('class', 'left-content');
96670 var body = helpContent.append('div').attr('class', 'body');
96671 var nav = helpContent.append('div').attr('class', 'nav');
96672 clickHelp(docs[0], 0);
96678 function uiSectionValidationIssues(id, severity, context) {
96680 var section = uiSection(id, context).label(function () {
96681 if (!_issues) return '';
96682 var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
96683 return _t('inspector.title_count', {
96684 title: _t.html('issues.' + severity + 's.list_title'),
96685 count: issueCountText
96687 }).disclosureContent(renderDisclosureContent).shouldDisplay(function () {
96688 return _issues && _issues.length;
96691 function getOptions() {
96693 what: corePreferences('validate-what') || 'edited',
96694 where: corePreferences('validate-where') || 'all'
96696 } // get and cache the issues to display, unordered
96699 function reloadIssues() {
96700 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
96703 function renderDisclosureContent(selection) {
96704 var center = context.map().center();
96705 var graph = context.graph(); // sort issues by distance away from the center of the map
96707 var issues = _issues.map(function withDistance(issue) {
96708 var extent = issue.extent(graph);
96709 var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
96710 return Object.assign(issue, {
96713 }).sort(function byDistance(a, b) {
96714 return a.dist - b.dist;
96715 }); // cut off at 1000
96718 issues = issues.slice(0, 1000); //renderIgnoredIssuesReset(_warningsSelection);
96720 selection.call(drawIssuesList, issues);
96723 function drawIssuesList(selection, issues) {
96724 var list = selection.selectAll('.issues-list').data([0]);
96725 list = list.enter().append('ul').attr('class', 'layer-list issues-list ' + severity + 's-list').merge(list);
96726 var items = list.selectAll('li').data(issues, function (d) {
96730 items.exit().remove(); // Enter
96732 var itemsEnter = items.enter().append('li').attr('class', function (d) {
96733 return 'issue severity-' + d.severity;
96735 var labelsEnter = itemsEnter.append('button').attr('class', 'issue-label').on('click', function (d3_event, d) {
96736 context.validator().focusIssue(d);
96737 }).on('mouseover', function (d3_event, d) {
96738 utilHighlightEntities(d.entityIds, true, context);
96739 }).on('mouseout', function (d3_event, d) {
96740 utilHighlightEntities(d.entityIds, false, context);
96742 var textEnter = labelsEnter.append('span').attr('class', 'issue-text');
96743 textEnter.append('span').attr('class', 'issue-icon').each(function (d) {
96744 var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
96745 select(this).call(svgIcon(iconName));
96747 textEnter.append('span').attr('class', 'issue-message');
96751 .attr('class', 'issue-autofix')
96752 .each(function(d) {
96753 if (!d.autoFix) return;
96756 .attr('title', t('issues.fix_one.title'))
96757 .datum(d.autoFix) // set button datum to the autofix
96758 .attr('class', 'autofix action')
96759 .on('click', function(d3_event, d) {
96760 d3_event.preventDefault();
96761 d3_event.stopPropagation();
96762 var issuesEntityIDs = d.issue.entityIds;
96763 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
96764 context.perform.apply(context, d.autoArgs);
96765 context.validator().validate();
96767 .call(svgIcon('#iD-icon-wrench'));
96772 items = items.merge(itemsEnter).order();
96773 items.selectAll('.issue-message').html(function (d) {
96774 return d.message(context);
96778 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
96779 var autoFixAll = selection.selectAll('.autofix-all')
96780 .data(canAutoFix.length ? [0] : []);
96785 var autoFixAllEnter = autoFixAll.enter()
96786 .insert('div', '.issues-list')
96787 .attr('class', 'autofix-all');
96788 var linkEnter = autoFixAllEnter
96790 .attr('class', 'autofix-all-link')
96791 .attr('href', '#');
96794 .attr('class', 'autofix-all-link-text')
96795 .html(t.html('issues.fix_all.title'));
96798 .attr('class', 'autofix-all-link-icon')
96799 .call(svgIcon('#iD-icon-wrench'));
96800 if (severity === 'warning') {
96801 renderIgnoredIssuesReset(selection);
96804 autoFixAll = autoFixAll
96805 .merge(autoFixAllEnter);
96806 autoFixAll.selectAll('.autofix-all-link')
96807 .on('click', function() {
96808 context.pauseChangeDispatch();
96809 context.perform(actionNoop());
96810 canAutoFix.forEach(function(issue) {
96811 var args = issue.autoFix.autoArgs.slice(); // copy
96812 if (typeof args[args.length - 1] !== 'function') {
96815 args.push(t('issues.fix_all.annotation'));
96816 context.replace.apply(context, args);
96818 context.resumeChangeDispatch();
96819 context.validator().validate();
96824 context.validator().on('validated.uiSectionValidationIssues' + id, function () {
96825 window.requestIdleCallback(function () {
96827 section.reRender();
96830 context.map().on('move.uiSectionValidationIssues' + id, debounce(function () {
96831 window.requestIdleCallback(function () {
96832 if (getOptions().where === 'visible') {
96833 // must refetch issues if they are viewport-dependent
96835 } // always reload list to re-sort-by-distance
96838 section.reRender();
96844 function uiSectionValidationOptions(context) {
96845 var section = uiSection('issues-options', context).content(renderContent);
96847 function renderContent(selection) {
96848 var container = selection.selectAll('.issues-options-container').data([0]);
96849 container = container.enter().append('div').attr('class', 'issues-options-container').merge(container);
96852 values: ['edited', 'all']
96855 values: ['visible', 'all']
96857 var options = container.selectAll('.issues-option').data(data, function (d) {
96860 var optionsEnter = options.enter().append('div').attr('class', function (d) {
96861 return 'issues-option issues-option-' + d.key;
96863 optionsEnter.append('div').attr('class', 'issues-option-title').html(function (d) {
96864 return _t.html('issues.options.' + d.key + '.title');
96866 var valuesEnter = optionsEnter.selectAll('label').data(function (d) {
96867 return d.values.map(function (val) {
96873 }).enter().append('label');
96874 valuesEnter.append('input').attr('type', 'radio').attr('name', function (d) {
96875 return 'issues-option-' + d.key;
96876 }).attr('value', function (d) {
96878 }).property('checked', function (d) {
96879 return getOptions()[d.key] === d.value;
96880 }).on('change', function (d3_event, d) {
96881 updateOptionValue(d3_event, d.key, d.value);
96883 valuesEnter.append('span').html(function (d) {
96884 return _t.html('issues.options.' + d.key + '.' + d.value);
96888 function getOptions() {
96890 what: corePreferences('validate-what') || 'edited',
96892 where: corePreferences('validate-where') || 'all' // 'all', 'visible'
96897 function updateOptionValue(d3_event, d, val) {
96898 if (!val && d3_event && d3_event.target) {
96899 val = d3_event.target.value;
96902 corePreferences('validate-' + d, val);
96903 context.validator().validate();
96909 function uiSectionValidationRules(context) {
96911 var MAXSQUARE = 20;
96912 var DEFAULTSQUARE = 5; // see also unsquare_way.js
96914 var section = uiSection('issues-rules', context).disclosureContent(renderDisclosureContent).label(_t.html('issues.rules.title'));
96916 var _ruleKeys = context.validator().getRuleKeys().filter(function (key) {
96917 return key !== 'maprules';
96918 }).sort(function (key1, key2) {
96919 // alphabetize by localized title
96920 return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
96923 function renderDisclosureContent(selection) {
96924 var container = selection.selectAll('.issues-rulelist-container').data([0]);
96925 var containerEnter = container.enter().append('div').attr('class', 'issues-rulelist-container');
96926 containerEnter.append('ul').attr('class', 'layer-list issue-rules-list');
96927 var ruleLinks = containerEnter.append('div').attr('class', 'issue-rules-links section-footer');
96928 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
96929 d3_event.preventDefault();
96930 context.validator().disableRules(_ruleKeys);
96932 ruleLinks.append('a').attr('class', 'issue-rules-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
96933 d3_event.preventDefault();
96934 context.validator().disableRules([]);
96937 container = container.merge(containerEnter);
96938 container.selectAll('.issue-rules-list').call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
96941 function drawListItems(selection, data, type, name, change, active) {
96942 var items = selection.selectAll('li').data(data); // Exit
96944 items.exit().remove(); // Enter
96946 var enter = items.enter().append('li');
96948 if (name === 'rule') {
96949 enter.call(uiTooltip().title(function (d) {
96950 return _t.html('issues.' + d + '.tip');
96951 }).placement('top'));
96954 var label = enter.append('label');
96955 label.append('input').attr('type', type).attr('name', name).on('change', change);
96956 label.append('span').html(function (d) {
96959 if (d === 'unsquare_way') {
96960 params.val = '<span class="square-degrees"></span>';
96963 return _t.html('issues.' + d + '.title', params);
96966 items = items.merge(enter);
96967 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false); // user-configurable square threshold
96969 var degStr = corePreferences('validate-square-degrees');
96971 if (degStr === null) {
96972 degStr = DEFAULTSQUARE.toString();
96975 var span = items.selectAll('.square-degrees');
96976 var input = span.selectAll('.square-degrees-input').data([0]); // enter / update
96978 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) {
96979 d3_event.preventDefault();
96980 d3_event.stopPropagation();
96982 }).on('keyup', function (d3_event) {
96983 if (d3_event.keyCode === 13) {
96988 }).on('blur', changeSquare).merge(input).property('value', degStr);
96991 function changeSquare() {
96992 var input = select(this);
96993 var degStr = utilGetSetValue(input).trim();
96994 var degNum = parseFloat(degStr, 10);
96996 if (!isFinite(degNum)) {
96997 degNum = DEFAULTSQUARE;
96998 } else if (degNum > MAXSQUARE) {
96999 degNum = MAXSQUARE;
97000 } else if (degNum < MINSQUARE) {
97001 degNum = MINSQUARE;
97004 degNum = Math.round(degNum * 10) / 10; // round to 1 decimal
97006 degStr = degNum.toString();
97007 input.property('value', degStr);
97008 corePreferences('validate-square-degrees', degStr);
97009 context.validator().reloadUnsquareIssues();
97012 function isRuleEnabled(d) {
97013 return context.validator().isRuleEnabled(d);
97016 function toggleRule(d3_event, d) {
97017 context.validator().toggleRule(d);
97020 context.validator().on('validated.uiSectionValidationRules', function () {
97021 window.requestIdleCallback(section.reRender);
97026 function uiSectionValidationStatus(context) {
97027 var section = uiSection('issues-status', context).content(renderContent).shouldDisplay(function () {
97028 var issues = context.validator().getIssues(getOptions());
97029 return issues.length === 0;
97032 function getOptions() {
97034 what: corePreferences('validate-what') || 'edited',
97035 where: corePreferences('validate-where') || 'all'
97039 function renderContent(selection) {
97040 var box = selection.selectAll('.box').data([0]);
97041 var boxEnter = box.enter().append('div').attr('class', 'box');
97042 boxEnter.append('div').call(svgIcon('#iD-icon-apply', 'pre-text'));
97043 var noIssuesMessage = boxEnter.append('span');
97044 noIssuesMessage.append('strong').attr('class', 'message');
97045 noIssuesMessage.append('br');
97046 noIssuesMessage.append('span').attr('class', 'details');
97047 renderIgnoredIssuesReset(selection);
97048 setNoIssuesText(selection);
97051 function renderIgnoredIssuesReset(selection) {
97052 var ignoredIssues = context.validator().getIssues({
97055 includeDisabledRules: true,
97056 includeIgnored: 'only'
97058 var resetIgnored = selection.selectAll('.reset-ignored').data(ignoredIssues.length ? [0] : []); // exit
97060 resetIgnored.exit().remove(); // enter
97062 var resetIgnoredEnter = resetIgnored.enter().append('div').attr('class', 'reset-ignored section-footer');
97063 resetIgnoredEnter.append('a').attr('href', '#'); // update
97065 resetIgnored = resetIgnored.merge(resetIgnoredEnter);
97066 resetIgnored.select('a').html(_t('inspector.title_count', {
97067 title: _t.html('issues.reset_ignored'),
97068 count: ignoredIssues.length
97070 resetIgnored.on('click', function (d3_event) {
97071 d3_event.preventDefault();
97072 context.validator().resetIgnoredIssues();
97076 function setNoIssuesText(selection) {
97077 var opts = getOptions();
97079 function checkForHiddenIssues(cases) {
97080 for (var type in cases) {
97081 var hiddenOpts = cases[type];
97082 var hiddenIssues = context.validator().getIssues(hiddenOpts);
97084 if (hiddenIssues.length) {
97085 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.' + type, {
97086 count: hiddenIssues.length.toString()
97092 selection.select('.box .details').html(_t.html('issues.no_issues.hidden_issues.none'));
97097 if (opts.what === 'edited' && opts.where === 'visible') {
97098 messageType = 'edits_in_view';
97099 checkForHiddenIssues({
97111 includeDisabledRules: 'only'
97113 everything_else_elsewhere: {
97117 disabled_rules_elsewhere: {
97120 includeDisabledRules: 'only'
97125 includeIgnored: 'only'
97127 ignored_issues_elsewhere: {
97130 includeIgnored: 'only'
97133 } else if (opts.what === 'edited' && opts.where === 'all') {
97134 messageType = 'edits';
97135 checkForHiddenIssues({
97143 includeDisabledRules: 'only'
97148 includeIgnored: 'only'
97151 } else if (opts.what === 'all' && opts.where === 'visible') {
97152 messageType = 'everything_in_view';
97153 checkForHiddenIssues({
97161 includeDisabledRules: 'only'
97163 disabled_rules_elsewhere: {
97166 includeDisabledRules: 'only'
97171 includeIgnored: 'only'
97173 ignored_issues_elsewhere: {
97176 includeIgnored: 'only'
97179 } else if (opts.what === 'all' && opts.where === 'all') {
97180 messageType = 'everything';
97181 checkForHiddenIssues({
97185 includeDisabledRules: 'only'
97190 includeIgnored: 'only'
97195 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
97196 messageType = 'no_edits';
97199 selection.select('.box .message').html(_t.html('issues.no_issues.message.' + messageType));
97202 context.validator().on('validated.uiSectionValidationStatus', function () {
97203 window.requestIdleCallback(section.reRender);
97205 context.map().on('move.uiSectionValidationStatus', debounce(function () {
97206 window.requestIdleCallback(section.reRender);
97211 function uiPaneIssues(context) {
97212 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)]);
97216 function uiSettingsCustomData(context) {
97217 var dispatch$1 = dispatch('change');
97219 function render(selection) {
97220 var dataLayer = context.layers().layer('data'); // keep separate copies of original and current settings
97222 var _origSettings = {
97223 fileList: dataLayer && dataLayer.fileList() || null,
97224 url: corePreferences('settings-custom-data-url')
97226 var _currSettings = {
97227 fileList: dataLayer && dataLayer.fileList() || null,
97228 url: corePreferences('settings-custom-data-url')
97229 }; // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
97231 var modal = uiConfirm(selection).okButton();
97232 modal.classed('settings-modal settings-custom-data', true);
97233 modal.select('.modal-section.header').append('h3').html(_t.html('settings.custom_data.header'));
97234 var textSection = modal.select('.modal-section.message-text');
97235 textSection.append('pre').attr('class', 'instructions-file').html(_t.html('settings.custom_data.file.instructions'));
97236 textSection.append('input').attr('class', 'field-file').attr('type', 'file').property('files', _currSettings.fileList) // works for all except IE11
97237 .on('change', function (d3_event) {
97238 var files = d3_event.target.files;
97240 if (files && files.length) {
97241 _currSettings.url = '';
97242 textSection.select('.field-url').property('value', '');
97243 _currSettings.fileList = files;
97245 _currSettings.fileList = null;
97248 textSection.append('h4').html(_t.html('settings.custom_data.or'));
97249 textSection.append('pre').attr('class', 'instructions-url').html(_t.html('settings.custom_data.url.instructions'));
97250 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
97252 var buttonSection = modal.select('.modal-section.buttons');
97253 buttonSection.insert('button', '.ok-button').attr('class', 'button cancel-button secondary-action').html(_t.html('confirm.cancel'));
97254 buttonSection.select('.cancel-button').on('click.cancel', clickCancel);
97255 buttonSection.select('.ok-button').attr('disabled', isSaveDisabled).on('click.save', clickSave);
97257 function isSaveDisabled() {
97259 } // restore the original url
97262 function clickCancel() {
97263 textSection.select('.field-url').property('value', _origSettings.url);
97264 corePreferences('settings-custom-data-url', _origSettings.url);
97267 } // accept the current url
97270 function clickSave() {
97271 _currSettings.url = textSection.select('.field-url').property('value').trim(); // one or the other but not both
97273 if (_currSettings.url) {
97274 _currSettings.fileList = null;
97277 if (_currSettings.fileList) {
97278 _currSettings.url = '';
97281 corePreferences('settings-custom-data-url', _currSettings.url);
97284 dispatch$1.call('change', this, _currSettings);
97288 return utilRebind(render, dispatch$1, 'on');
97291 function uiSectionDataLayers(context) {
97292 var settingsCustomData = uiSettingsCustomData(context).on('change', customChanged);
97293 var layers = context.layers();
97294 var section = uiSection('data-layers', context).label(_t.html('map_data.data_layers')).disclosureContent(renderDisclosureContent);
97296 function renderDisclosureContent(selection) {
97297 var container = selection.selectAll('.data-layer-container').data([0]);
97298 container.enter().append('div').attr('class', 'data-layer-container').merge(container).call(drawOsmItems).call(drawQAItems).call(drawCustomDataItems).call(drawVectorItems) // Beta - Detroit mapping challenge
97299 .call(drawPanelItems);
97302 function showsLayer(which) {
97303 var layer = layers.layer(which);
97306 return layer.enabled();
97312 function setLayer(which, enabled) {
97313 // Don't allow layer changes while drawing - #6584
97314 var mode = context.mode();
97315 if (mode && /^draw/.test(mode.id)) return;
97316 var layer = layers.layer(which);
97319 layer.enabled(enabled);
97321 if (!enabled && (which === 'osm' || which === 'notes')) {
97322 context.enter(modeBrowse(context));
97327 function toggleLayer(which) {
97328 setLayer(which, !showsLayer(which));
97331 function drawOsmItems(selection) {
97332 var osmKeys = ['osm', 'notes'];
97333 var osmLayers = layers.all().filter(function (obj) {
97334 return osmKeys.indexOf(obj.id) !== -1;
97336 var ul = selection.selectAll('.layer-list-osm').data([0]);
97337 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-osm').merge(ul);
97338 var li = ul.selectAll('.list-item').data(osmLayers);
97339 li.exit().remove();
97340 var liEnter = li.enter().append('li').attr('class', function (d) {
97341 return 'list-item list-item-' + d.id;
97343 var labelEnter = liEnter.append('label').each(function (d) {
97344 if (d.id === 'osm') {
97345 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))]).placement('bottom'));
97347 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
97350 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97353 labelEnter.append('span').html(function (d) {
97354 return _t.html('map_data.layers.' + d.id + '.title');
97357 li.merge(liEnter).classed('active', function (d) {
97358 return d.layer.enabled();
97359 }).selectAll('input').property('checked', function (d) {
97360 return d.layer.enabled();
97364 function drawQAItems(selection) {
97365 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
97366 var qaLayers = layers.all().filter(function (obj) {
97367 return qaKeys.indexOf(obj.id) !== -1;
97369 var ul = selection.selectAll('.layer-list-qa').data([0]);
97370 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-qa').merge(ul);
97371 var li = ul.selectAll('.list-item').data(qaLayers);
97372 li.exit().remove();
97373 var liEnter = li.enter().append('li').attr('class', function (d) {
97374 return 'list-item list-item-' + d.id;
97376 var labelEnter = liEnter.append('label').each(function (d) {
97377 select(this).call(uiTooltip().title(_t.html('map_data.layers.' + d.id + '.tooltip')).placement('bottom'));
97379 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97382 labelEnter.append('span').html(function (d) {
97383 return _t.html('map_data.layers.' + d.id + '.title');
97386 li.merge(liEnter).classed('active', function (d) {
97387 return d.layer.enabled();
97388 }).selectAll('input').property('checked', function (d) {
97389 return d.layer.enabled();
97391 } // Beta feature - sample vector layers to support Detroit Mapping Challenge
97392 // https://github.com/osmus/detroit-mapping-challenge
97395 function drawVectorItems(selection) {
97396 var dataLayer = layers.layer('data');
97398 name: 'Detroit Neighborhoods/Parks',
97399 src: 'neighborhoods-parks',
97400 tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
97401 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'
97403 name: 'Detroit Composite POIs',
97404 src: 'composite-poi',
97405 tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
97406 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'
97408 name: 'Detroit All-The-Places POIs',
97409 src: 'alltheplaces-poi',
97410 tooltip: 'Public domain business location data created by web scrapers.',
97411 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'
97412 }]; // Only show this if the map is around Detroit..
97414 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
97415 var showVectorItems = context.map().zoom() > 9 && detroit.contains(context.map().center());
97416 var container = selection.selectAll('.vectortile-container').data(showVectorItems ? [0] : []);
97417 container.exit().remove();
97418 var containerEnter = container.enter().append('div').attr('class', 'vectortile-container');
97419 containerEnter.append('h4').attr('class', 'vectortile-header').html('Detroit Vector Tiles (Beta)');
97420 containerEnter.append('ul').attr('class', 'layer-list layer-list-vectortile');
97421 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');
97422 container = container.merge(containerEnter);
97423 var ul = container.selectAll('.layer-list-vectortile');
97424 var li = ul.selectAll('.list-item').data(vtData);
97425 li.exit().remove();
97426 var liEnter = li.enter().append('li').attr('class', function (d) {
97427 return 'list-item list-item-' + d.src;
97429 var labelEnter = liEnter.append('label').each(function (d) {
97430 select(this).call(uiTooltip().title(d.tooltip).placement('top'));
97432 labelEnter.append('input').attr('type', 'radio').attr('name', 'vectortile').on('change', selectVTLayer);
97433 labelEnter.append('span').html(function (d) {
97437 li.merge(liEnter).classed('active', isVTLayerSelected).selectAll('input').property('checked', isVTLayerSelected);
97439 function isVTLayerSelected(d) {
97440 return dataLayer && dataLayer.template() === d.template;
97443 function selectVTLayer(d3_event, d) {
97444 corePreferences('settings-custom-data-url', d.template);
97447 dataLayer.template(d.template, d.src);
97448 dataLayer.enabled(true);
97453 function drawCustomDataItems(selection) {
97454 var dataLayer = layers.layer('data');
97455 var hasData = dataLayer && dataLayer.hasData();
97456 var showsData = hasData && dataLayer.enabled();
97457 var ul = selection.selectAll('.layer-list-data').data(dataLayer ? [0] : []); // Exit
97459 ul.exit().remove(); // Enter
97461 var ulEnter = ul.enter().append('ul').attr('class', 'layer-list layer-list-data');
97462 var liEnter = ulEnter.append('li').attr('class', 'list-item-data');
97463 var labelEnter = liEnter.append('label').call(uiTooltip().title(_t.html('map_data.layers.custom.tooltip')).placement('top'));
97464 labelEnter.append('input').attr('type', 'checkbox').on('change', function () {
97465 toggleLayer('data');
97467 labelEnter.append('span').html(_t.html('map_data.layers.custom.title'));
97468 liEnter.append('button').attr('class', 'open-data-options').call(uiTooltip().title(_t.html('settings.custom_data.tooltip')).placement(_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')).on('click', function (d3_event) {
97469 d3_event.preventDefault();
97471 }).call(svgIcon('#iD-icon-more'));
97472 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) {
97473 if (select(this).classed('disabled')) return;
97474 d3_event.preventDefault();
97475 d3_event.stopPropagation();
97476 dataLayer.fitZoom();
97477 }).call(svgIcon('#iD-icon-framed-dot', 'monochrome')); // Update
97479 ul = ul.merge(ulEnter);
97480 ul.selectAll('.list-item-data').classed('active', showsData).selectAll('label').classed('deemphasize', !hasData).selectAll('input').property('disabled', !hasData).property('checked', showsData);
97481 ul.selectAll('button.zoom-to-data').classed('disabled', !hasData);
97484 function editCustom() {
97485 context.container().call(settingsCustomData);
97488 function customChanged(d) {
97489 var dataLayer = layers.layer('data');
97492 dataLayer.url(d.url);
97493 } else if (d && d.fileList) {
97494 dataLayer.fileList(d.fileList);
97498 function drawPanelItems(selection) {
97499 var panelsListEnter = selection.selectAll('.md-extras-list').data([0]).enter().append('ul').attr('class', 'layer-list md-extras-list');
97500 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'));
97501 historyPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97502 d3_event.preventDefault();
97503 context.ui().info.toggle('history');
97505 historyPanelLabelEnter.append('span').html(_t.html('map_data.history_panel.title'));
97506 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'));
97507 measurementPanelLabelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97508 d3_event.preventDefault();
97509 context.ui().info.toggle('measurement');
97511 measurementPanelLabelEnter.append('span').html(_t.html('map_data.measurement_panel.title'));
97514 context.layers().on('change.uiSectionDataLayers', section.reRender);
97515 context.map().on('move.uiSectionDataLayers', debounce(function () {
97516 // Detroit layers may have moved in or out of view
97517 window.requestIdleCallback(section.reRender);
97522 function uiSectionMapFeatures(context) {
97523 var _features = context.features().keys();
97525 var section = uiSection('map-features', context).label(_t.html('map_data.map_features')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97527 function renderDisclosureContent(selection) {
97528 var container = selection.selectAll('.layer-feature-list-container').data([0]);
97529 var containerEnter = container.enter().append('div').attr('class', 'layer-feature-list-container');
97530 containerEnter.append('ul').attr('class', 'layer-list layer-feature-list');
97531 var footer = containerEnter.append('div').attr('class', 'feature-list-links section-footer');
97532 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.disable_all')).on('click', function (d3_event) {
97533 d3_event.preventDefault();
97534 context.features().disableAll();
97536 footer.append('a').attr('class', 'feature-list-link').attr('href', '#').html(_t.html('issues.enable_all')).on('click', function (d3_event) {
97537 d3_event.preventDefault();
97538 context.features().enableAll();
97541 container = container.merge(containerEnter);
97542 container.selectAll('.layer-feature-list').call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
97545 function drawListItems(selection, data, type, name, change, active) {
97546 var items = selection.selectAll('li').data(data); // Exit
97548 items.exit().remove(); // Enter
97550 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
97551 var tip = _t.html(name + '.' + d + '.tooltip');
97553 if (autoHiddenFeature(d)) {
97554 var msg = showsLayer('osm') ? _t.html('map_data.autohidden') : _t.html('map_data.osmhidden');
97555 tip += '<div>' + msg + '</div>';
97559 }).placement('top'));
97560 var label = enter.append('label');
97561 label.append('input').attr('type', type).attr('name', name).on('change', change);
97562 label.append('span').html(function (d) {
97563 return _t.html(name + '.' + d + '.description');
97566 items = items.merge(enter);
97567 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', autoHiddenFeature);
97570 function autoHiddenFeature(d) {
97571 return context.features().autoHidden(d);
97574 function showsFeature(d) {
97575 return context.features().enabled(d);
97578 function clickFeature(d3_event, d) {
97579 context.features().toggle(d);
97582 function showsLayer(id) {
97583 var layer = context.layers().layer(id);
97584 return layer && layer.enabled();
97588 context.features().on('change.map_features', section.reRender);
97592 function uiSectionMapStyleOptions(context) {
97593 var section = uiSection('fill-area', context).label(_t.html('map_data.style_options')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97595 function renderDisclosureContent(selection) {
97596 var container = selection.selectAll('.layer-fill-list').data([0]);
97597 container.enter().append('ul').attr('class', 'layer-list layer-fill-list').merge(container).call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
97598 var container2 = selection.selectAll('.layer-visual-diff-list').data([0]);
97599 container2.enter().append('ul').attr('class', 'layer-list layer-visual-diff-list').merge(container2).call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function () {
97600 return context.surface().classed('highlight-edited');
97604 function drawListItems(selection, data, type, name, change, active) {
97605 var items = selection.selectAll('li').data(data); // Exit
97607 items.exit().remove(); // Enter
97609 var enter = items.enter().append('li').call(uiTooltip().title(function (d) {
97610 return _t.html(name + '.' + d + '.tooltip');
97611 }).keys(function (d) {
97612 var key = d === 'wireframe' ? _t('area_fill.wireframe.key') : null;
97613 if (d === 'highlight_edits') key = _t('map_data.highlight_edits.key');
97614 return key ? [key] : null;
97615 }).placement('top'));
97616 var label = enter.append('label');
97617 label.append('input').attr('type', type).attr('name', name).on('change', change);
97618 label.append('span').html(function (d) {
97619 return _t.html(name + '.' + d + '.description');
97622 items = items.merge(enter);
97623 items.classed('active', active).selectAll('input').property('checked', active).property('indeterminate', false);
97626 function isActiveFill(d) {
97627 return context.map().activeAreaFill() === d;
97630 function toggleHighlightEdited(d3_event) {
97631 d3_event.preventDefault();
97632 context.map().toggleHighlightEdited();
97635 function setFill(d3_event, d) {
97636 context.map().activeAreaFill(d);
97639 context.map().on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
97643 function uiSectionPhotoOverlays(context) {
97644 var layers = context.layers();
97645 var section = uiSection('photo-overlays', context).label(_t.html('photo_overlays.title')).disclosureContent(renderDisclosureContent).expandedByDefault(false);
97647 function renderDisclosureContent(selection) {
97648 var container = selection.selectAll('.photo-overlay-container').data([0]);
97649 container.enter().append('div').attr('class', 'photo-overlay-container').merge(container).call(drawPhotoItems).call(drawPhotoTypeItems).call(drawDateFilter).call(drawUsernameFilter);
97652 function drawPhotoItems(selection) {
97653 var photoKeys = context.photos().overlayLayerIDs();
97654 var photoLayers = layers.all().filter(function (obj) {
97655 return photoKeys.indexOf(obj.id) !== -1;
97657 var data = photoLayers.filter(function (obj) {
97658 return obj.layer.supported();
97661 function layerSupported(d) {
97662 return d.layer && d.layer.supported();
97665 function layerEnabled(d) {
97666 return layerSupported(d) && d.layer.enabled();
97669 var ul = selection.selectAll('.layer-list-photos').data([0]);
97670 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photos').merge(ul);
97671 var li = ul.selectAll('.list-item-photos').data(data);
97672 li.exit().remove();
97673 var liEnter = li.enter().append('li').attr('class', function (d) {
97674 var classes = 'list-item-photos list-item-' + d.id;
97676 if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
97677 classes += ' indented';
97682 var labelEnter = liEnter.append('label').each(function (d) {
97684 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';
97685 select(this).call(uiTooltip().title(_t.html(titleID)).placement('top'));
97687 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97690 labelEnter.append('span').html(function (d) {
97692 if (id === 'mapillary-signs') id = 'photo_overlays.traffic_signs';
97693 return _t.html(id.replace(/-/g, '_') + '.title');
97696 li.merge(liEnter).classed('active', layerEnabled).selectAll('input').property('checked', layerEnabled);
97699 function drawPhotoTypeItems(selection) {
97700 var data = context.photos().allPhotoTypes();
97702 function typeEnabled(d) {
97703 return context.photos().showsPhotoType(d);
97706 var ul = selection.selectAll('.layer-list-photo-types').data([0]);
97707 ul.exit().remove();
97708 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-photo-types').merge(ul);
97709 var li = ul.selectAll('.list-item-photo-types').data(context.photos().shouldFilterByPhotoType() ? data : []);
97710 li.exit().remove();
97711 var liEnter = li.enter().append('li').attr('class', function (d) {
97712 return 'list-item-photo-types list-item-' + d;
97714 var labelEnter = liEnter.append('label').each(function (d) {
97715 select(this).call(uiTooltip().title(_t.html('photo_overlays.photo_type.' + d + '.tooltip')).placement('top'));
97717 labelEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event, d) {
97718 context.photos().togglePhotoType(d);
97720 labelEnter.append('span').html(function (d) {
97721 return _t.html('photo_overlays.photo_type.' + d + '.title');
97724 li.merge(liEnter).classed('active', typeEnabled).selectAll('input').property('checked', typeEnabled);
97727 function drawDateFilter(selection) {
97728 var data = context.photos().dateFilters();
97730 function filterEnabled(d) {
97731 return context.photos().dateFilterValue(d);
97734 var ul = selection.selectAll('.layer-list-date-filter').data([0]);
97735 ul.exit().remove();
97736 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-date-filter').merge(ul);
97737 var li = ul.selectAll('.list-item-date-filter').data(context.photos().shouldFilterByDate() ? data : []);
97738 li.exit().remove();
97739 var liEnter = li.enter().append('li').attr('class', 'list-item-date-filter');
97740 var labelEnter = liEnter.append('label').each(function (d) {
97741 select(this).call(uiTooltip().title(_t.html('photo_overlays.date_filter.' + d + '.tooltip')).placement('top'));
97743 labelEnter.append('span').html(function (d) {
97744 return _t.html('photo_overlays.date_filter.' + d + '.title');
97746 labelEnter.append('input').attr('type', 'date').attr('class', 'list-item-input').attr('placeholder', _t('units.year_month_day')).call(utilNoAuto).each(function (d) {
97747 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
97748 }).on('change', function (d3_event, d) {
97749 var value = utilGetSetValue(select(this)).trim();
97750 context.photos().setDateFilter(d, value, true); // reload the displayed dates
97752 li.selectAll('input').each(function (d) {
97753 utilGetSetValue(select(this), context.photos().dateFilterValue(d) || '');
97756 li = li.merge(liEnter).classed('active', filterEnabled);
97759 function drawUsernameFilter(selection) {
97760 function filterEnabled() {
97761 return context.photos().usernames();
97764 var ul = selection.selectAll('.layer-list-username-filter').data([0]);
97765 ul.exit().remove();
97766 ul = ul.enter().append('ul').attr('class', 'layer-list layer-list-username-filter').merge(ul);
97767 var li = ul.selectAll('.list-item-username-filter').data(context.photos().shouldFilterByUsername() ? ['username-filter'] : []);
97768 li.exit().remove();
97769 var liEnter = li.enter().append('li').attr('class', 'list-item-username-filter');
97770 var labelEnter = liEnter.append('label').each(function () {
97771 select(this).call(uiTooltip().title(_t.html('photo_overlays.username_filter.tooltip')).placement('top'));
97773 labelEnter.append('span').html(_t.html('photo_overlays.username_filter.title'));
97774 labelEnter.append('input').attr('type', 'text').attr('class', 'list-item-input').call(utilNoAuto).property('value', usernameValue).on('change', function () {
97775 var value = select(this).property('value');
97776 context.photos().setUsernameFilter(value, true);
97777 select(this).property('value', usernameValue);
97779 li.merge(liEnter).classed('active', filterEnabled);
97781 function usernameValue() {
97782 var usernames = context.photos().usernames();
97783 if (usernames) return usernames.join('; ');
97788 function toggleLayer(which) {
97789 setLayer(which, !showsLayer(which));
97792 function showsLayer(which) {
97793 var layer = layers.layer(which);
97796 return layer.enabled();
97802 function setLayer(which, enabled) {
97803 var layer = layers.layer(which);
97806 layer.enabled(enabled);
97810 context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
97811 context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
97815 function uiPaneMapData(context) {
97816 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)]);
97817 return mapDataPane;
97820 function uiSectionPrivacy(context) {
97821 var section = uiSection('preferences-third-party', context).label(_t.html('preferences.privacy.title')).disclosureContent(renderDisclosureContent);
97823 var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
97825 function renderDisclosureContent(selection) {
97827 var privacyOptionsListEnter = selection.selectAll('.privacy-options-list').data([0]).enter().append('ul').attr('class', 'layer-list privacy-options-list');
97828 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'));
97829 thirdPartyIconsEnter.append('input').attr('type', 'checkbox').on('change', function (d3_event) {
97830 d3_event.preventDefault();
97831 _showThirdPartyIcons = _showThirdPartyIcons === 'true' ? 'false' : 'true';
97832 corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
97835 thirdPartyIconsEnter.append('span').html(_t.html('preferences.privacy.third_party_icons.description')); // Privacy Policy link
97837 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'));
97840 function update() {
97841 selection.selectAll('.privacy-third-party-icons-item').classed('active', _showThirdPartyIcons === 'true').select('input').property('checked', _showThirdPartyIcons === 'true');
97848 function uiPanePreferences(context) {
97849 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)]);
97850 return preferencesPane;
97853 function uiInit(context) {
97854 var _initCounter = 0;
97855 var _needWidth = {};
97857 var _lastPointerType;
97859 function render(container) {
97860 container.on('click.ui', function (d3_event) {
97861 // we're only concerned with the primary mouse button
97862 if (d3_event.button !== 0) return;
97863 if (!d3_event.composedPath) return; // some targets have default click events we don't want to override
97865 var isOkayTarget = d3_event.composedPath().some(function (node) {
97866 // we only care about element nodes
97867 return node.nodeType === 1 && ( // clicking <input> focuses it and/or changes a value
97868 node.nodeName === 'INPUT' || // clicking <label> affects its <input> by default
97869 node.nodeName === 'LABEL' || // clicking <a> opens a hyperlink by default
97870 node.nodeName === 'A');
97872 if (isOkayTarget) return; // disable double-tap-to-zoom on touchscreens
97874 d3_event.preventDefault();
97876 var detected = utilDetect(); // only WebKit supports gesture events
97878 if ('GestureEvent' in window && // Listening for gesture events on iOS 13.4+ breaks double-tapping,
97879 // but we only need to do this on desktop Safari anyway. – #7694
97880 !detected.isMobileWebKit) {
97881 // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
97882 // CSS property, but on desktop Safari we need to manually cancel the
97883 // default gesture events.
97884 container.on('gesturestart.ui gesturechange.ui gestureend.ui', function (d3_event) {
97885 // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
97886 d3_event.preventDefault();
97890 if ('PointerEvent' in window) {
97891 select(window).on('pointerdown.ui pointerup.ui', function (d3_event) {
97892 var pointerType = d3_event.pointerType || 'mouse';
97894 if (_lastPointerType !== pointerType) {
97895 _lastPointerType = pointerType;
97896 container.attr('pointer', pointerType);
97900 _lastPointerType = 'mouse';
97901 container.attr('pointer', 'mouse');
97904 container.attr('lang', _mainLocalizer.localeCode()).attr('dir', _mainLocalizer.textDirection()); // setup fullscreen keybindings (no button shown at this time)
97906 container.call(uiFullScreen(context));
97907 var map = context.map();
97908 map.redrawEnable(false); // don't draw until we've set zoom/lat/long
97910 map.on('hitMinZoom.ui', function () {
97911 ui.flash.iconName('#iD-icon-no').label(_t.html('cannot_zoom'))();
97913 container.append('svg').attr('id', 'ideditor-defs').call(ui.svgDefs);
97914 container.append('div').attr('class', 'sidebar').call(ui.sidebar);
97915 var content = container.append('div').attr('class', 'main-content active'); // Top toolbar
97917 content.append('div').attr('class', 'top-toolbar-wrap').append('div').attr('class', 'top-toolbar fillD').call(uiTopToolbar(context));
97918 content.append('div').attr('class', 'main-map').attr('dir', 'ltr').call(map);
97919 var overMap = content.append('div').attr('class', 'over-map'); // HACK: Mobile Safari 14 likes to select anything selectable when long-
97920 // pressing, even if it's not targeted. This conflicts with long-pressing
97921 // to show the edit menu. We add a selectable offscreen element as the first
97922 // child to trick Safari into not showing the selection UI.
97924 overMap.append('div').attr('class', 'select-trap').text('t');
97925 overMap.call(uiMapInMap(context)).call(uiNotice(context));
97926 overMap.append('div').attr('class', 'spinner').call(uiSpinner(context)); // Map controls
97928 var controls = overMap.append('div').attr('class', 'map-controls');
97929 controls.append('div').attr('class', 'map-control zoombuttons').call(uiZoom(context));
97930 controls.append('div').attr('class', 'map-control zoom-to-selection-control').call(uiZoomToSelection(context));
97931 controls.append('div').attr('class', 'map-control geolocate-control').call(uiGeolocate(context)); // Add panes
97932 // This should happen after map is initialized, as some require surface()
97934 var panes = overMap.append('div').attr('class', 'map-panes');
97935 var uiPanes = [uiPaneBackground(context), uiPaneMapData(context), uiPaneIssues(context), uiPanePreferences(context), uiPaneHelp(context)];
97936 uiPanes.forEach(function (pane) {
97937 controls.append('div').attr('class', 'map-control map-pane-control ' + pane.id + '-control').call(pane.renderToggleButton);
97938 panes.call(pane.renderPane);
97940 ui.info = uiInfo(context);
97941 overMap.call(ui.info);
97942 overMap.append('div').attr('class', 'photoviewer').classed('al', true) // 'al'=left, 'ar'=right
97943 .classed('hide', true).call(ui.photoviewer);
97944 overMap.append('div').attr('class', 'attribution-wrap').attr('dir', 'ltr').call(uiAttribution(context)); // Add footer
97946 var about = content.append('div').attr('class', 'map-footer');
97947 about.append('div').attr('class', 'api-status').call(uiStatus(context));
97948 var footer = about.append('div').attr('class', 'map-footer-bar fillD');
97949 footer.append('div').attr('class', 'flash-wrap footer-hide');
97950 var footerWrap = footer.append('div').attr('class', 'main-footer-wrap footer-show');
97951 footerWrap.append('div').attr('class', 'scale-block').call(uiScale(context));
97952 var aboutList = footerWrap.append('div').attr('class', 'info-block').append('ul').attr('class', 'map-footer-list');
97953 aboutList.append('li').attr('class', 'user-list').call(uiContributors(context));
97954 var apiConnections = context.apiConnections();
97956 if (apiConnections && apiConnections.length > 1) {
97957 aboutList.append('li').attr('class', 'source-switch').call(uiSourceSwitch(context).keys(apiConnections));
97960 aboutList.append('li').attr('class', 'issues-info').call(uiIssuesInfo(context));
97961 aboutList.append('li').attr('class', 'feature-warning').call(uiFeatureInfo(context));
97962 var issueLinks = aboutList.append('li');
97963 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'));
97964 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'));
97965 aboutList.append('li').attr('class', 'version').call(uiVersion(context));
97967 if (!context.embed()) {
97968 aboutList.call(uiAccount(context));
97969 } // Setup map dimensions and move map to initial center/zoom.
97970 // This should happen after .main-content and toolbars exist.
97974 map.redrawEnable(true);
97975 ui.hash = behaviorHash(context);
97978 if (!ui.hash.hadHash) {
97979 map.centerZoom([0, 0], 2);
97983 window.onbeforeunload = function () {
97984 return context.save();
97987 window.onunload = function () {
97988 context.history().unlock();
97991 select(window).on('resize.editor', function () {
97994 var panPixels = 80;
97995 context.keybinding().on('⌫', function (d3_event) {
97996 d3_event.preventDefault();
97997 }).on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle) // #5663, #6864 - common QWERTY, AZERTY
97998 .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) {
98000 d3_event.stopImmediatePropagation();
98001 d3_event.preventDefault();
98004 var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
98006 if (previousBackground) {
98007 var currentBackground = context.background().baseLayerSource();
98008 corePreferences('background-last-used-toggle', currentBackground.id);
98009 corePreferences('background-last-used', previousBackground.id);
98010 context.background().baseLayerSource(previousBackground);
98012 }).on(_t('area_fill.wireframe.key'), function toggleWireframe(d3_event) {
98013 d3_event.preventDefault();
98014 d3_event.stopPropagation();
98015 context.map().toggleWireframe();
98016 }).on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData(d3_event) {
98017 d3_event.preventDefault();
98018 d3_event.stopPropagation(); // Don't allow layer changes while drawing - #6584
98020 var mode = context.mode();
98021 if (mode && /^draw/.test(mode.id)) return;
98022 var layer = context.layers().layer('osm');
98025 layer.enabled(!layer.enabled());
98027 if (!layer.enabled()) {
98028 context.enter(modeBrowse(context));
98031 }).on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited(d3_event) {
98032 d3_event.preventDefault();
98033 context.map().toggleHighlightEdited();
98035 context.on('enter.editor', function (entered) {
98036 container.classed('mode-' + entered.id, true);
98037 }).on('exit.editor', function (exited) {
98038 container.classed('mode-' + exited.id, false);
98040 context.enter(modeBrowse(context));
98042 if (!_initCounter++) {
98043 if (!ui.hash.startWalkthrough) {
98044 context.container().call(uiSplash(context)).call(uiRestore(context));
98047 context.container().call(ui.shortcuts);
98050 var osm = context.connection();
98051 var auth = uiLoading(context).message(_t.html('loading_auth')).blocking(true);
98054 osm.on('authLoading.ui', function () {
98055 context.container().call(auth);
98056 }).on('authDone.ui', function () {
98063 if (ui.hash.startWalkthrough) {
98064 ui.hash.startWalkthrough = false;
98065 context.container().call(uiIntro(context));
98069 return function (d3_event) {
98070 if (d3_event.shiftKey) return;
98071 if (context.container().select('.combobox').size()) return;
98072 d3_event.preventDefault();
98073 context.map().pan(d, 100);
98080 var _loadPromise; // renders the iD interface into the container node
98083 ui.ensureLoaded = function () {
98084 if (_loadPromise) return _loadPromise;
98085 return _loadPromise = Promise.all([// must have strings and presets before loading the UI
98086 _mainLocalizer.ensureLoaded(), _mainPresetIndex.ensureLoaded()]).then(function () {
98087 if (!context.container().empty()) render(context.container());
98088 })["catch"](function (err) {
98089 return console.error(err);
98090 }); // eslint-disable-line
98091 }; // `ui.restart()` will destroy and rebuild the entire iD interface,
98092 // for example to switch the locale while iD is running.
98095 ui.restart = function () {
98096 context.keybinding().clear();
98097 _loadPromise = null;
98098 context.container().selectAll('*').remove();
98102 ui.lastPointerType = function () {
98103 return _lastPointerType;
98106 ui.svgDefs = svgDefs(context);
98107 ui.flash = uiFlash(context);
98108 ui.sidebar = uiSidebar(context);
98109 ui.photoviewer = uiPhotoviewer(context);
98110 ui.shortcuts = uiShortcuts(context);
98112 ui.onResize = function (withPan) {
98113 var map = context.map(); // Recalc dimensions of map and sidebar.. (`true` = force recalc)
98114 // This will call `getBoundingClientRect` and trigger reflow,
98115 // but the values will be cached for later use.
98117 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
98118 utilGetDimensions(context.container().select('.sidebar'), true);
98120 if (withPan !== undefined) {
98121 map.redrawEnable(false);
98123 map.redrawEnable(true);
98126 map.dimensions(mapDimensions);
98127 ui.photoviewer.onMapResize(); // check if header or footer have overflowed
98129 ui.checkOverflow('.top-toolbar');
98130 ui.checkOverflow('.map-footer-bar'); // Use outdated code so it works on Explorer
98132 var resizeWindowEvent = document.createEvent('Event');
98133 resizeWindowEvent.initEvent('resizeWindow', true, true);
98134 document.dispatchEvent(resizeWindowEvent);
98135 }; // Call checkOverflow when resizing or whenever the contents change.
98138 ui.checkOverflow = function (selector, reset) {
98140 delete _needWidth[selector];
98143 var selection = context.container().select(selector);
98144 if (selection.empty()) return;
98145 var scrollWidth = selection.property('scrollWidth');
98146 var clientWidth = selection.property('clientWidth');
98147 var needed = _needWidth[selector] || scrollWidth;
98149 if (scrollWidth > clientWidth) {
98150 // overflow happening
98151 selection.classed('narrow', true);
98153 if (!_needWidth[selector]) {
98154 _needWidth[selector] = scrollWidth;
98156 } else if (scrollWidth >= needed) {
98157 selection.classed('narrow', false);
98161 ui.togglePanes = function (showPane) {
98162 var hidePanes = context.container().selectAll('.map-pane.shown');
98163 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
98164 hidePanes.classed('shown', false).classed('hide', true);
98165 context.container().selectAll('.map-pane-control button').classed('active', false);
98168 hidePanes.classed('shown', false).classed('hide', true).style(side, '-500px');
98169 context.container().selectAll('.' + showPane.attr('pane') + '-control button').classed('active', true);
98170 showPane.classed('shown', true).classed('hide', false);
98172 if (hidePanes.empty()) {
98173 showPane.style(side, '-500px').transition().duration(200).style(side, '0px');
98175 showPane.style(side, '0px');
98178 hidePanes.classed('shown', true).classed('hide', false).style(side, '0px').transition().duration(200).style(side, '-500px').on('end', function () {
98179 select(this).classed('shown', false).classed('hide', true);
98184 var _editMenu = uiEditMenu(context);
98186 ui.editMenu = function () {
98190 ui.showEditMenu = function (anchorPoint, triggerType, operations) {
98191 // remove any displayed menu
98192 ui.closeEditMenu();
98193 if (!operations && context.mode().operations) operations = context.mode().operations();
98194 if (!operations || !operations.length) return; // disable menu if in wide selection, for example
98196 if (!context.map().editableDataEnabled()) return;
98197 var surfaceNode = context.surface().node();
98199 if (surfaceNode.focus) {
98200 // FF doesn't support it
98201 // focus the surface or else clicking off the menu may not trigger modeBrowse
98202 surfaceNode.focus();
98205 operations.forEach(function (operation) {
98206 if (operation.point) operation.point(anchorPoint);
98209 _editMenu.anchorLoc(anchorPoint).triggerType(triggerType).operations(operations); // render the menu
98212 context.map().supersurface.call(_editMenu);
98215 ui.closeEditMenu = function () {
98216 // remove any existing menu no matter how it was added
98217 context.map().supersurface.select('.edit-menu').remove();
98220 var _saveLoading = select(null);
98222 context.uploader().on('saveStarted.ui', function () {
98223 _saveLoading = uiLoading(context).message(_t.html('save.uploading')).blocking(true);
98224 context.container().call(_saveLoading); // block input during upload
98225 }).on('saveEnded.ui', function () {
98226 _saveLoading.close();
98228 _saveLoading = select(null);
98233 function coreContext() {
98236 var dispatch$1 = dispatch('enter', 'exit', 'change');
98237 var context = utilRebind({}, dispatch$1, 'on');
98239 var _deferred = new Set();
98241 context.version = '2.19.4';
98242 context.privacyVersion = '20200407'; // iD will alter the hash so cache the parameters intended to setup the session
98244 context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
98245 context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
98247 // An osmChangeset object. Not loaded until needed.
98249 context.changeset = null;
98250 var _defaultChangesetComment = context.initialHashParams.comment;
98251 var _defaultChangesetSource = context.initialHashParams.source;
98252 var _defaultChangesetHashtags = context.initialHashParams.hashtags;
98254 context.defaultChangesetComment = function (val) {
98255 if (!arguments.length) return _defaultChangesetComment;
98256 _defaultChangesetComment = val;
98260 context.defaultChangesetSource = function (val) {
98261 if (!arguments.length) return _defaultChangesetSource;
98262 _defaultChangesetSource = val;
98266 context.defaultChangesetHashtags = function (val) {
98267 if (!arguments.length) return _defaultChangesetHashtags;
98268 _defaultChangesetHashtags = val;
98271 /* Document title */
98273 /* (typically shown as the label for the browser window/tab) */
98274 // If true, iD will update the title based on what the user is doing
98277 var _setsDocumentTitle = true;
98279 context.setsDocumentTitle = function (val) {
98280 if (!arguments.length) return _setsDocumentTitle;
98281 _setsDocumentTitle = val;
98283 }; // The part of the title that is always the same
98286 var _documentTitleBase = document.title;
98288 context.documentTitleBase = function (val) {
98289 if (!arguments.length) return _documentTitleBase;
98290 _documentTitleBase = val;
98293 /* User interface and keybinding */
98298 context.ui = function () {
98302 context.lastPointerType = function () {
98303 return _ui.lastPointerType();
98306 var _keybinding = utilKeybinding('context');
98308 context.keybinding = function () {
98309 return _keybinding;
98312 select(document).call(_keybinding);
98313 /* Straight accessors. Avoid using these if you can. */
98314 // Instantiate the connection here because it doesn't require passing in
98315 // `context` and it's needed for pre-init calls like `preauth`
98317 var _connection = services.osm;
98325 context.connection = function () {
98326 return _connection;
98329 context.history = function () {
98333 context.validator = function () {
98337 context.uploader = function () {
98343 context.preauth = function (options) {
98345 _connection["switch"](options);
98350 /* connection options for source switcher (optional) */
98353 var _apiConnections;
98355 context.apiConnections = function (val) {
98356 if (!arguments.length) return _apiConnections;
98357 _apiConnections = val;
98359 }; // A string or array or locale codes to prefer over the browser's settings
98362 context.locale = function (locale) {
98363 if (!arguments.length) return _mainLocalizer.localeCode();
98364 _mainLocalizer.preferredLocaleCodes(locale);
98368 function afterLoad(cid, callback) {
98369 return function (err, result) {
98371 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
98372 if (err.status === 400 || err.status === 401 || err.status === 403) {
98374 _connection.logout();
98378 if (typeof callback === 'function') {
98383 } else if (_connection && _connection.getConnectionId() !== cid) {
98384 if (typeof callback === 'function') {
98386 message: 'Connection Switched',
98393 _history.merge(result.data, result.extent);
98395 if (typeof callback === 'function') {
98396 callback(err, result);
98404 context.loadTiles = function (projection, callback) {
98405 var handle = window.requestIdleCallback(function () {
98406 _deferred["delete"](handle);
98408 if (_connection && context.editableDataEnabled()) {
98409 var cid = _connection.getConnectionId();
98411 _connection.loadTiles(projection, afterLoad(cid, callback));
98415 _deferred.add(handle);
98418 context.loadTileAtLoc = function (loc, callback) {
98419 var handle = window.requestIdleCallback(function () {
98420 _deferred["delete"](handle);
98422 if (_connection && context.editableDataEnabled()) {
98423 var cid = _connection.getConnectionId();
98425 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
98429 _deferred.add(handle);
98432 context.loadEntity = function (entityID, callback) {
98434 var cid = _connection.getConnectionId();
98436 _connection.loadEntity(entityID, afterLoad(cid, callback));
98440 context.zoomToEntity = function (entityID, zoomTo) {
98441 if (zoomTo !== false) {
98442 context.loadEntity(entityID, function (err, result) {
98444 var entity = result.data.find(function (e) {
98445 return e.id === entityID;
98449 _map.zoomTo(entity);
98454 _map.on('drawn.zoomToEntity', function () {
98455 if (!context.hasEntity(entityID)) return;
98457 _map.on('drawn.zoomToEntity', null);
98459 context.on('enter.zoomToEntity', null);
98460 context.enter(modeSelect(context, [entityID]));
98463 context.on('enter.zoomToEntity', function () {
98464 if (_mode.id !== 'browse') {
98465 _map.on('drawn.zoomToEntity', null);
98467 context.on('enter.zoomToEntity', null);
98472 var _minEditableZoom = 16;
98474 context.minEditableZoom = function (val) {
98475 if (!arguments.length) return _minEditableZoom;
98476 _minEditableZoom = val;
98479 _connection.tileZoom(val);
98483 }; // String length limits in Unicode characters, not JavaScript UTF-16 code units
98486 context.maxCharsForTagKey = function () {
98490 context.maxCharsForTagValue = function () {
98494 context.maxCharsForRelationRole = function () {
98498 function cleanOsmString(val, maxChars) {
98499 // be lenient with input
98500 if (val === undefined || val === null) {
98503 val = val.toString();
98504 } // remove whitespace
98507 val = val.trim(); // use the canonical form of the string
98509 if (val.normalize) val = val.normalize('NFC'); // trim to the number of allowed characters
98511 return utilUnicodeCharsTruncated(val, maxChars);
98514 context.cleanTagKey = function (val) {
98515 return cleanOsmString(val, context.maxCharsForTagKey());
98518 context.cleanTagValue = function (val) {
98519 return cleanOsmString(val, context.maxCharsForTagValue());
98522 context.cleanRelationRole = function (val) {
98523 return cleanOsmString(val, context.maxCharsForRelationRole());
98528 var _inIntro = false;
98530 context.inIntro = function (val) {
98531 if (!arguments.length) return _inIntro;
98534 }; // Immediately save the user's history to localstorage, if possible
98535 // This is called someteimes, but also on the `window.onbeforeunload` handler
98538 context.save = function () {
98539 // no history save, no message onbeforeunload
98540 if (_inIntro || context.container().select('.modal').size()) return;
98543 if (_mode && _mode.id === 'save') {
98544 canSave = false; // Attempt to prevent user from creating duplicate changes - see #5200
98546 if (services.osm && services.osm.isChangesetInflight()) {
98547 _history.clearSaved();
98552 canSave = context.selectedIDs().every(function (id) {
98553 var entity = context.hasEntity(id);
98554 return entity && !entity.isDegenerate();
98562 if (_history.hasChanges()) {
98563 return _t('save.unsaved_changes');
98565 }; // Debounce save, since it's a synchronous localStorage write,
98566 // and history changes can happen frequently (e.g. when dragging).
98569 context.debouncedSave = debounce(context.save, 350);
98571 function withDebouncedSave(fn) {
98572 return function () {
98573 var result = fn.apply(_history, arguments);
98574 context.debouncedSave();
98581 context.hasEntity = function (id) {
98582 return _history.graph().hasEntity(id);
98585 context.entity = function (id) {
98586 return _history.graph().entity(id);
98593 context.mode = function () {
98597 context.enter = function (newMode) {
98601 dispatch$1.call('exit', _this, _mode);
98608 dispatch$1.call('enter', _this, _mode);
98611 context.selectedIDs = function () {
98612 return _mode && _mode.selectedIDs && _mode.selectedIDs() || [];
98615 context.activeID = function () {
98616 return _mode && _mode.activeID && _mode.activeID();
98619 var _selectedNoteID;
98621 context.selectedNoteID = function (noteID) {
98622 if (!arguments.length) return _selectedNoteID;
98623 _selectedNoteID = noteID;
98625 }; // NOTE: Don't change the name of this until UI v3 is merged
98628 var _selectedErrorID;
98630 context.selectedErrorID = function (errorID) {
98631 if (!arguments.length) return _selectedErrorID;
98632 _selectedErrorID = errorID;
98638 context.install = function (behavior) {
98639 return context.surface().call(behavior);
98642 context.uninstall = function (behavior) {
98643 return context.surface().call(behavior.off);
98650 context.copyGraph = function () {
98656 context.copyIDs = function (val) {
98657 if (!arguments.length) return _copyIDs;
98659 _copyGraph = _history.graph();
98665 context.copyLonLat = function (val) {
98666 if (!arguments.length) return _copyLonLat;
98675 context.background = function () {
98676 return _background;
98683 context.features = function () {
98687 context.hasHiddenConnections = function (id) {
98688 var graph = _history.graph();
98690 var entity = graph.entity(id);
98691 return _features.hasHiddenConnections(entity, graph);
98698 context.photos = function () {
98706 context.map = function () {
98710 context.layers = function () {
98711 return _map.layers();
98714 context.surface = function () {
98715 return _map.surface;
98718 context.editableDataEnabled = function () {
98719 return _map.editableDataEnabled();
98722 context.surfaceRect = function () {
98723 return _map.surface.node().getBoundingClientRect();
98726 context.editable = function () {
98727 // don't allow editing during save
98728 var mode = context.mode();
98729 if (!mode || mode.id === 'save') return false;
98730 return _map.editableDataEnabled();
98735 var _debugFlags = {
98739 // label collision bounding boxes
98741 // imagery bounding polygons
98744 downloaded: false // downloaded data from osm
98748 context.debugFlags = function () {
98749 return _debugFlags;
98752 context.getDebug = function (flag) {
98753 return flag && _debugFlags[flag];
98756 context.setDebug = function (flag, val) {
98757 if (arguments.length === 1) val = true;
98758 _debugFlags[flag] = val;
98759 dispatch$1.call('change');
98765 var _container = select(null);
98767 context.container = function (val) {
98768 if (!arguments.length) return _container;
98771 _container.classed('ideditor', true);
98776 context.containerNode = function (val) {
98777 if (!arguments.length) return context.container().node();
98778 context.container(select(val));
98784 context.embed = function (val) {
98785 if (!arguments.length) return _embed;
98792 var _assetPath = '';
98794 context.assetPath = function (val) {
98795 if (!arguments.length) return _assetPath;
98797 _mainFileFetcher.assetPath(val);
98801 var _assetMap = {};
98803 context.assetMap = function (val) {
98804 if (!arguments.length) return _assetMap;
98806 _mainFileFetcher.assetMap(val);
98810 context.asset = function (val) {
98811 if (/^http(s)?:\/\//i.test(val)) return val;
98812 var filename = _assetPath + val;
98813 return _assetMap[filename] || filename;
98816 context.imagePath = function (val) {
98817 return context.asset("img/".concat(val));
98819 /* reset (aka flush) */
98822 context.reset = context.flush = function () {
98823 context.debouncedSave.cancel();
98824 Array.from(_deferred).forEach(function (handle) {
98825 window.cancelIdleCallback(handle);
98827 _deferred["delete"](handle);
98829 Object.values(services).forEach(function (service) {
98830 if (service && typeof service.reset === 'function') {
98831 service.reset(context);
98834 context.changeset = null;
98836 _validator.reset();
98842 _uploader.reset(); // don't leave stale state in the inspector
98845 context.container().select('.inspector-wrap *').remove();
98851 context.projection = geoRawMercator();
98852 context.curtainProjection = geoRawMercator();
98855 context.init = function () {
98856 instantiateInternal();
98857 initializeDependents();
98858 return context; // Load variables and properties. No property of `context` should be accessed
98859 // until this is complete since load statuses are indeterminate. The order
98860 // of instantiation shouldn't matter.
98862 function instantiateInternal() {
98863 _history = coreHistory(context);
98864 context.graph = _history.graph;
98865 context.pauseChangeDispatch = _history.pauseChangeDispatch;
98866 context.resumeChangeDispatch = _history.resumeChangeDispatch;
98867 context.perform = withDebouncedSave(_history.perform);
98868 context.replace = withDebouncedSave(_history.replace);
98869 context.pop = withDebouncedSave(_history.pop);
98870 context.overwrite = withDebouncedSave(_history.overwrite);
98871 context.undo = withDebouncedSave(_history.undo);
98872 context.redo = withDebouncedSave(_history.redo);
98873 _validator = coreValidator(context);
98874 _uploader = coreUploader(context);
98875 _background = rendererBackground(context);
98876 _features = rendererFeatures(context);
98877 _map = rendererMap(context);
98878 _photos = rendererPhotos(context);
98879 _ui = uiInit(context);
98880 } // Set up objects that might need to access properties of `context`. The order
98881 // might matter if dependents make calls to each other. Be wary of async calls.
98884 function initializeDependents() {
98885 if (context.initialHashParams.presets) {
98886 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
98889 if (context.initialHashParams.locale) {
98890 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
98891 } // kick off some async work
98894 _mainLocalizer.ensureLoaded();
98896 _background.ensureLoaded();
98898 _mainPresetIndex.ensureLoaded();
98899 Object.values(services).forEach(function (service) {
98900 if (service && typeof service.init === 'function') {
98911 if (services.maprules && context.initialHashParams.maprules) {
98912 d3_json(context.initialHashParams.maprules).then(function (mapcss) {
98913 services.maprules.init();
98914 mapcss.forEach(function (mapcssSelector) {
98915 return services.maprules.addRule(mapcssSelector);
98917 })["catch"](function () {
98920 } // if the container isn't available, e.g. when testing, don't load the UI
98923 if (!context.container().empty()) {
98924 _ui.ensureLoaded().then(function () {
98934 // This is only done in testing because of the performance penalty.
98936 var debug = false; // Reexport just what our tests use, see #4379
98938 dispatch: dispatch,
98939 geoMercator: mercator,
98940 geoProjection: projection,
98941 polygonArea: d3_polygonArea,
98942 polygonCentroid: d3_polygonCentroid,
98944 selectAll: selectAll,
98945 timerFlush: timerFlush
98948 var iD = /*#__PURE__*/Object.freeze({
98952 actionAddEntity: actionAddEntity,
98953 actionAddMember: actionAddMember,
98954 actionAddMidpoint: actionAddMidpoint,
98955 actionAddVertex: actionAddVertex,
98956 actionChangeMember: actionChangeMember,
98957 actionChangePreset: actionChangePreset,
98958 actionChangeTags: actionChangeTags,
98959 actionCircularize: actionCircularize,
98960 actionConnect: actionConnect,
98961 actionCopyEntities: actionCopyEntities,
98962 actionDeleteMember: actionDeleteMember,
98963 actionDeleteMultiple: actionDeleteMultiple,
98964 actionDeleteNode: actionDeleteNode,
98965 actionDeleteRelation: actionDeleteRelation,
98966 actionDeleteWay: actionDeleteWay,
98967 actionDiscardTags: actionDiscardTags,
98968 actionDisconnect: actionDisconnect,
98969 actionExtract: actionExtract,
98970 actionJoin: actionJoin,
98971 actionMerge: actionMerge,
98972 actionMergeNodes: actionMergeNodes,
98973 actionMergePolygon: actionMergePolygon,
98974 actionMergeRemoteChanges: actionMergeRemoteChanges,
98975 actionMove: actionMove,
98976 actionMoveMember: actionMoveMember,
98977 actionMoveNode: actionMoveNode,
98978 actionNoop: actionNoop,
98979 actionOrthogonalize: actionOrthogonalize,
98980 actionRestrictTurn: actionRestrictTurn,
98981 actionReverse: actionReverse,
98982 actionRevert: actionRevert,
98983 actionRotate: actionRotate,
98984 actionScale: actionScale,
98985 actionSplit: actionSplit,
98986 actionStraightenNodes: actionStraightenNodes,
98987 actionStraightenWay: actionStraightenWay,
98988 actionUnrestrictTurn: actionUnrestrictTurn,
98989 actionReflect: actionReflect,
98990 actionUpgradeTags: actionUpgradeTags,
98991 behaviorAddWay: behaviorAddWay,
98992 behaviorBreathe: behaviorBreathe,
98993 behaviorDrag: behaviorDrag,
98994 behaviorDrawWay: behaviorDrawWay,
98995 behaviorDraw: behaviorDraw,
98996 behaviorEdit: behaviorEdit,
98997 behaviorHash: behaviorHash,
98998 behaviorHover: behaviorHover,
98999 behaviorLasso: behaviorLasso,
99000 behaviorOperation: behaviorOperation,
99001 behaviorPaste: behaviorPaste,
99002 behaviorSelect: behaviorSelect,
99003 coreContext: coreContext,
99004 coreFileFetcher: coreFileFetcher,
99005 fileFetcher: _mainFileFetcher,
99006 coreDifference: coreDifference,
99007 coreGraph: coreGraph,
99008 coreHistory: coreHistory,
99009 coreLocalizer: coreLocalizer,
99011 localizer: _mainLocalizer,
99012 prefs: corePreferences,
99013 coreTree: coreTree,
99014 coreUploader: coreUploader,
99015 coreValidator: coreValidator,
99016 geoExtent: geoExtent,
99017 geoLatToMeters: geoLatToMeters,
99018 geoLonToMeters: geoLonToMeters,
99019 geoMetersToLat: geoMetersToLat,
99020 geoMetersToLon: geoMetersToLon,
99021 geoMetersToOffset: geoMetersToOffset,
99022 geoOffsetToMeters: geoOffsetToMeters,
99023 geoScaleToZoom: geoScaleToZoom,
99024 geoSphericalClosestNode: geoSphericalClosestNode,
99025 geoSphericalDistance: geoSphericalDistance,
99026 geoZoomToScale: geoZoomToScale,
99027 geoAngle: geoAngle,
99028 geoChooseEdge: geoChooseEdge,
99029 geoEdgeEqual: geoEdgeEqual,
99030 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
99031 geoHasLineIntersections: geoHasLineIntersections,
99032 geoHasSelfIntersections: geoHasSelfIntersections,
99033 geoRotate: geoRotate,
99034 geoLineIntersection: geoLineIntersection,
99035 geoPathHasIntersections: geoPathHasIntersections,
99036 geoPathIntersections: geoPathIntersections,
99037 geoPathLength: geoPathLength,
99038 geoPointInPolygon: geoPointInPolygon,
99039 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
99040 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
99041 geoViewportEdge: geoViewportEdge,
99042 geoRawMercator: geoRawMercator,
99043 geoVecAdd: geoVecAdd,
99044 geoVecAngle: geoVecAngle,
99045 geoVecCross: geoVecCross,
99046 geoVecDot: geoVecDot,
99047 geoVecEqual: geoVecEqual,
99048 geoVecFloor: geoVecFloor,
99049 geoVecInterp: geoVecInterp,
99050 geoVecLength: geoVecLength,
99051 geoVecLengthSquare: geoVecLengthSquare,
99052 geoVecNormalize: geoVecNormalize,
99053 geoVecNormalizedDot: geoVecNormalizedDot,
99054 geoVecProject: geoVecProject,
99055 geoVecSubtract: geoVecSubtract,
99056 geoVecScale: geoVecScale,
99057 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
99058 geoOrthoCalcScore: geoOrthoCalcScore,
99059 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
99060 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
99061 modeAddArea: modeAddArea,
99062 modeAddLine: modeAddLine,
99063 modeAddPoint: modeAddPoint,
99064 modeAddNote: modeAddNote,
99065 modeBrowse: modeBrowse,
99066 modeDragNode: modeDragNode,
99067 modeDragNote: modeDragNote,
99068 modeDrawArea: modeDrawArea,
99069 modeDrawLine: modeDrawLine,
99070 modeMove: modeMove,
99071 modeRotate: modeRotate,
99072 modeSave: modeSave,
99073 modeSelect: modeSelect,
99074 modeSelectData: modeSelectData,
99075 modeSelectError: modeSelectError,
99076 modeSelectNote: modeSelectNote,
99077 operationCircularize: operationCircularize,
99078 operationContinue: operationContinue,
99079 operationCopy: operationCopy,
99080 operationDelete: operationDelete,
99081 operationDisconnect: operationDisconnect,
99082 operationDowngrade: operationDowngrade,
99083 operationExtract: operationExtract,
99084 operationMerge: operationMerge,
99085 operationMove: operationMove,
99086 operationOrthogonalize: operationOrthogonalize,
99087 operationPaste: operationPaste,
99088 operationReflectShort: operationReflectShort,
99089 operationReflectLong: operationReflectLong,
99090 operationReverse: operationReverse,
99091 operationRotate: operationRotate,
99092 operationSplit: operationSplit,
99093 operationStraighten: operationStraighten,
99094 osmChangeset: osmChangeset,
99095 osmEntity: osmEntity,
99098 osmRelation: osmRelation,
99101 osmIntersection: osmIntersection,
99103 osmInferRestriction: osmInferRestriction,
99104 osmLanes: osmLanes,
99105 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
99106 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
99107 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
99108 osmJoinWays: osmJoinWays,
99109 get osmAreaKeys () { return osmAreaKeys; },
99110 osmSetAreaKeys: osmSetAreaKeys,
99111 osmTagSuggestingArea: osmTagSuggestingArea,
99112 get osmPointTags () { return osmPointTags; },
99113 osmSetPointTags: osmSetPointTags,
99114 get osmVertexTags () { return osmVertexTags; },
99115 osmSetVertexTags: osmSetVertexTags,
99116 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
99117 osmOneWayTags: osmOneWayTags,
99118 osmPavedTags: osmPavedTags,
99119 osmIsInterestingTag: osmIsInterestingTag,
99120 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
99121 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
99122 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
99123 presetCategory: presetCategory,
99124 presetCollection: presetCollection,
99125 presetField: presetField,
99126 presetPreset: presetPreset,
99127 presetManager: _mainPresetIndex,
99128 presetIndex: presetIndex,
99129 rendererBackgroundSource: rendererBackgroundSource,
99130 rendererBackground: rendererBackground,
99131 rendererFeatures: rendererFeatures,
99132 rendererMap: rendererMap,
99133 rendererPhotos: rendererPhotos,
99134 rendererTileLayer: rendererTileLayer,
99135 services: services,
99136 serviceKeepRight: serviceKeepRight,
99137 serviceImproveOSM: serviceImproveOSM,
99138 serviceOsmose: serviceOsmose,
99139 serviceMapillary: serviceMapillary,
99140 serviceMapRules: serviceMapRules,
99141 serviceNominatim: serviceNominatim,
99142 serviceOpenstreetcam: serviceOpenstreetcam,
99143 serviceOsm: serviceOsm,
99144 serviceOsmWikibase: serviceOsmWikibase,
99145 serviceStreetside: serviceStreetside,
99146 serviceTaginfo: serviceTaginfo,
99147 serviceVectorTile: serviceVectorTile,
99148 serviceWikidata: serviceWikidata,
99149 serviceWikipedia: serviceWikipedia,
99150 svgAreas: svgAreas,
99152 svgDebug: svgDebug,
99154 svgKeepRight: svgKeepRight,
99156 svgGeolocate: svgGeolocate,
99157 svgLabels: svgLabels,
99158 svgLayers: svgLayers,
99159 svgLines: svgLines,
99160 svgMapillaryImages: svgMapillaryImages,
99161 svgMapillarySigns: svgMapillarySigns,
99162 svgMidpoints: svgMidpoints,
99163 svgNotes: svgNotes,
99164 svgMarkerSegments: svgMarkerSegments,
99165 svgOpenstreetcamImages: svgOpenstreetcamImages,
99167 svgPassiveVertex: svgPassiveVertex,
99169 svgPointTransform: svgPointTransform,
99170 svgPoints: svgPoints,
99171 svgRelationMemberTags: svgRelationMemberTags,
99172 svgSegmentWay: svgSegmentWay,
99173 svgStreetside: svgStreetside,
99174 svgTagClasses: svgTagClasses,
99175 svgTagPattern: svgTagPattern,
99176 svgTouch: svgTouch,
99177 svgTurns: svgTurns,
99178 svgVertices: svgVertices,
99179 uiFieldDefaultCheck: uiFieldCheck,
99180 uiFieldOnewayCheck: uiFieldCheck,
99181 uiFieldCheck: uiFieldCheck,
99182 uiFieldManyCombo: uiFieldCombo,
99183 uiFieldMultiCombo: uiFieldCombo,
99184 uiFieldNetworkCombo: uiFieldCombo,
99185 uiFieldSemiCombo: uiFieldCombo,
99186 uiFieldTypeCombo: uiFieldCombo,
99187 uiFieldCombo: uiFieldCombo,
99188 uiFieldUrl: uiFieldText,
99189 uiFieldIdentifier: uiFieldText,
99190 uiFieldNumber: uiFieldText,
99191 uiFieldTel: uiFieldText,
99192 uiFieldEmail: uiFieldText,
99193 uiFieldText: uiFieldText,
99194 uiFieldAccess: uiFieldAccess,
99195 uiFieldAddress: uiFieldAddress,
99196 uiFieldCycleway: uiFieldCycleway,
99197 uiFieldLanes: uiFieldLanes,
99198 uiFieldLocalized: uiFieldLocalized,
99199 uiFieldMaxspeed: uiFieldMaxspeed,
99200 uiFieldStructureRadio: uiFieldRadio,
99201 uiFieldRadio: uiFieldRadio,
99202 uiFieldRestrictions: uiFieldRestrictions,
99203 uiFieldTextarea: uiFieldTextarea,
99204 uiFieldWikidata: uiFieldWikidata,
99205 uiFieldWikipedia: uiFieldWikipedia,
99206 uiFields: uiFields,
99208 uiPanelBackground: uiPanelBackground,
99209 uiPanelHistory: uiPanelHistory,
99210 uiPanelLocation: uiPanelLocation,
99211 uiPanelMeasurement: uiPanelMeasurement,
99212 uiInfoPanels: uiInfoPanels,
99213 uiPaneBackground: uiPaneBackground,
99214 uiPaneHelp: uiPaneHelp,
99215 uiPaneIssues: uiPaneIssues,
99216 uiPaneMapData: uiPaneMapData,
99217 uiPanePreferences: uiPanePreferences,
99218 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
99219 uiSectionBackgroundList: uiSectionBackgroundList,
99220 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
99221 uiSectionChanges: uiSectionChanges,
99222 uiSectionDataLayers: uiSectionDataLayers,
99223 uiSectionEntityIssues: uiSectionEntityIssues,
99224 uiSectionFeatureType: uiSectionFeatureType,
99225 uiSectionMapFeatures: uiSectionMapFeatures,
99226 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
99227 uiSectionOverlayList: uiSectionOverlayList,
99228 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
99229 uiSectionPresetFields: uiSectionPresetFields,
99230 uiSectionPrivacy: uiSectionPrivacy,
99231 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
99232 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
99233 uiSectionRawTagEditor: uiSectionRawTagEditor,
99234 uiSectionSelectionList: uiSectionSelectionList,
99235 uiSectionValidationIssues: uiSectionValidationIssues,
99236 uiSectionValidationOptions: uiSectionValidationOptions,
99237 uiSectionValidationRules: uiSectionValidationRules,
99238 uiSectionValidationStatus: uiSectionValidationStatus,
99239 uiSettingsCustomBackground: uiSettingsCustomBackground,
99240 uiSettingsCustomData: uiSettingsCustomData,
99242 uiAccount: uiAccount,
99243 uiAttribution: uiAttribution,
99244 uiChangesetEditor: uiChangesetEditor,
99246 uiCombobox: uiCombobox,
99247 uiCommit: uiCommit,
99248 uiCommitWarnings: uiCommitWarnings,
99249 uiConfirm: uiConfirm,
99250 uiConflicts: uiConflicts,
99251 uiContributors: uiContributors,
99252 uiCurtain: uiCurtain,
99253 uiDataEditor: uiDataEditor,
99254 uiDataHeader: uiDataHeader,
99255 uiDisclosure: uiDisclosure,
99256 uiEditMenu: uiEditMenu,
99257 uiEntityEditor: uiEntityEditor,
99258 uiFeatureInfo: uiFeatureInfo,
99259 uiFeatureList: uiFeatureList,
99261 uiFieldHelp: uiFieldHelp,
99263 uiFormFields: uiFormFields,
99264 uiFullScreen: uiFullScreen,
99265 uiGeolocate: uiGeolocate,
99266 uiImproveOsmComments: uiImproveOsmComments,
99267 uiImproveOsmDetails: uiImproveOsmDetails,
99268 uiImproveOsmEditor: uiImproveOsmEditor,
99269 uiImproveOsmHeader: uiImproveOsmHeader,
99271 uiInspector: uiInspector,
99272 uiIssuesInfo: uiIssuesInfo,
99273 uiKeepRightDetails: uiKeepRightDetails,
99274 uiKeepRightEditor: uiKeepRightEditor,
99275 uiKeepRightHeader: uiKeepRightHeader,
99277 uiLoading: uiLoading,
99278 uiMapInMap: uiMapInMap,
99280 uiNotice: uiNotice,
99281 uiNoteComments: uiNoteComments,
99282 uiNoteEditor: uiNoteEditor,
99283 uiNoteHeader: uiNoteHeader,
99284 uiNoteReport: uiNoteReport,
99285 uiPopover: uiPopover,
99286 uiPresetIcon: uiPresetIcon,
99287 uiPresetList: uiPresetList,
99288 uiRestore: uiRestore,
99290 uiSidebar: uiSidebar,
99291 uiSourceSwitch: uiSourceSwitch,
99292 uiSpinner: uiSpinner,
99293 uiSplash: uiSplash,
99294 uiStatus: uiStatus,
99295 uiSuccess: uiSuccess,
99296 uiTagReference: uiTagReference,
99297 uiToggle: uiToggle,
99298 uiTooltip: uiTooltip,
99299 uiVersion: uiVersion,
99300 uiViewOnOSM: uiViewOnOSM,
99301 uiViewOnKeepRight: uiViewOnKeepRight,
99303 utilAesEncrypt: utilAesEncrypt,
99304 utilAesDecrypt: utilAesDecrypt,
99305 utilArrayChunk: utilArrayChunk,
99306 utilArrayDifference: utilArrayDifference,
99307 utilArrayFlatten: utilArrayFlatten,
99308 utilArrayGroupBy: utilArrayGroupBy,
99309 utilArrayIdentical: utilArrayIdentical,
99310 utilArrayIntersection: utilArrayIntersection,
99311 utilArrayUnion: utilArrayUnion,
99312 utilArrayUniq: utilArrayUniq,
99313 utilArrayUniqBy: utilArrayUniqBy,
99314 utilAsyncMap: utilAsyncMap,
99315 utilCleanTags: utilCleanTags,
99316 utilCombinedTags: utilCombinedTags,
99317 utilDeepMemberSelector: utilDeepMemberSelector,
99318 utilDetect: utilDetect,
99319 utilDisplayName: utilDisplayName,
99320 utilDisplayNameForPath: utilDisplayNameForPath,
99321 utilDisplayType: utilDisplayType,
99322 utilDisplayLabel: utilDisplayLabel,
99323 utilEntityRoot: utilEntityRoot,
99324 utilEditDistance: utilEditDistance,
99325 utilEntitySelector: utilEntitySelector,
99326 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
99327 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
99328 utilFastMouse: utilFastMouse,
99329 utilFunctor: utilFunctor,
99330 utilGetAllNodes: utilGetAllNodes,
99331 utilGetSetValue: utilGetSetValue,
99332 utilHashcode: utilHashcode,
99333 utilHighlightEntities: utilHighlightEntities,
99334 utilKeybinding: utilKeybinding,
99335 utilNoAuto: utilNoAuto,
99336 utilObjectOmit: utilObjectOmit,
99337 utilPrefixCSSProperty: utilPrefixCSSProperty,
99338 utilPrefixDOMProperty: utilPrefixDOMProperty,
99339 utilQsString: utilQsString,
99340 utilRebind: utilRebind,
99341 utilSafeClassName: utilSafeClassName,
99342 utilSetTransform: utilSetTransform,
99343 utilSessionMutex: utilSessionMutex,
99344 utilStringQs: utilStringQs,
99345 utilTagDiff: utilTagDiff,
99346 utilTagText: utilTagText,
99347 utilTiler: utilTiler,
99348 utilTotalExtent: utilTotalExtent,
99349 utilTriggerEvent: utilTriggerEvent,
99350 utilUnicodeCharsCount: utilUnicodeCharsCount,
99351 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
99352 utilUniqueDomId: utilUniqueDomId,
99353 utilWrap: utilWrap,
99354 validationAlmostJunction: validationAlmostJunction,
99355 validationCloseNodes: validationCloseNodes,
99356 validationCrossingWays: validationCrossingWays,
99357 validationDisconnectedWay: validationDisconnectedWay,
99358 validationFormatting: validationFormatting,
99359 validationHelpRequest: validationHelpRequest,
99360 validationImpossibleOneway: validationImpossibleOneway,
99361 validationIncompatibleSource: validationIncompatibleSource,
99362 validationMaprules: validationMaprules,
99363 validationMismatchedGeometry: validationMismatchedGeometry,
99364 validationMissingRole: validationMissingRole,
99365 validationMissingTag: validationMissingTag,
99366 validationOutdatedTags: validationOutdatedTags,
99367 validationPrivateData: validationPrivateData,
99368 validationSuspiciousName: validationSuspiciousName,
99369 validationUnsquareWay: validationUnsquareWay
99372 window.requestIdleCallback = window.requestIdleCallback || function (cb) {
99373 var start = Date.now();
99374 return window.requestAnimationFrame(function () {
99377 timeRemaining: function timeRemaining() {
99378 return Math.max(0, 50 - (Date.now() - start));
99384 window.cancelIdleCallback = window.cancelIdleCallback || function (id) {
99385 window.cancelAnimationFrame(id);